diff --git a/.pi/memory-vault/packs/paperclip-harness/20-context/both-defects-fixed-and-the-settings-editor-is-in.md b/.pi/memory-vault/packs/paperclip-harness/20-context/both-defects-fixed-and-the-settings-editor-is-in.md new file mode 100644 index 0000000..918e950 --- /dev/null +++ b/.pi/memory-vault/packs/paperclip-harness/20-context/both-defects-fixed-and-the-settings-editor-is-in.md @@ -0,0 +1,78 @@ +--- +type: context +id: context.paperclip-harness.both-defects-fixed-and-the-settings-editor-is-in +title: both-defects-fixed-and-the-settings-editor-is-in +status: active +source_of_truth: false +freshness: current +last_reviewed: 2026-11-10 +tags: + - pack/paperclip-harness + - agent-memory/context + - autolearn + - rich-discovery + - repo/both-defects-fixed-and-the-settings-editor-is-in +--- + +# both-defects-fixed-and-the-settings-editor-is-in + +## Rich discovery update (2026-11-10 06:23:51) + +Both defects fixed, and the settings editor is in. + +## The three things you reported + +**Runs were every run in Paperclip.** Correct — 281 company-wide. They are now scoped to this project's tasks, resolved through each run's own task reference, and the task column shows `AUD-166` instead of a bare identifier. `A` toggles between "this project" and "everything in the company", because both are legitimate questions but not the same one. + +**Scrolling instead of selecting.** The run list had no cursor at all — arrows only moved the viewport. There is now one cursor per tab, the selected row is highlighted, and the list scrolls only to keep the cursor visible. `enter` opens that run's log. Tested: picked the second run, its log opened. + +**Roster could not be walked.** Same cause, same fix. The roster is sorted by name so the cursor stays on the same agent after a reload, and `▸` marks the row. + +The log itself also became readable: it is a stream of token deltas, so deltas are gathered back into paragraphs while tool calls, prompts and turn markers become single lines: + +``` +15:55:57 toolcall_start +15:55:57 {"command": "pwd && ls -la && git log --oneline -10 2>/dev/null"} +15:55:58 toolcall_end +15:55:58 assistant: I'll start by inspecting the workspace… +``` + +## Agents>settings is built + +Fourth tab. Role, job title, model, monthly budget, heartbeat. `enter` edits the chosen field inline, `esc` cancels; heartbeat is a toggle. Every save is one `PATCH /api/agents/{id}` that merges into that agent's existing configuration, so a model change does not wipe its working folder or instructions — verified by reading the record back after a save. + +`p`, `P`, `w` pause, resume and wake, acting on the agent chosen on that tab. + +**One thing you should know:** `Builder` was paused at 16:56 with `pauseReason: manual`, from the board key — that was my keystroke test, not you. I have resumed it and it is idle again. Sorry for the scare. + +## Still missing, in order + +1. **Work>review** — the ocr verdict beside the diff. That is now the only missing panel. +2. **Tool panes** — `t` opens Lazygit, Hunk, Yazi, Neovim, fzf or ocr in the current workspace; `esc` closes it. + +`docs/WORKSPACES.md` is updated: 22 panels built across 8 workspaces, one panel missing, six tool panes to wire. Committed as `ec7e907`. + +Which of those two next? + +## Source paths observed + +- `omni/deepseek/deepseek-v4-flash` + +## Evidence + +- Confidence: 84% +- Reason: Update component/repository context with detailed discovered structure and configuration + +## Related + +- [[packs/paperclip-harness/00-system/pi-agent/memory-manifest|Memory Manifest]] + +## Related learned memories + +- context: [[packs/paperclip-harness/20-context/tmp|Paperclip TUI Agents pane]] +- context: [[packs/paperclip-harness/20-context/paperclip-tui-workspaces-panels-and-tools-status|Paperclip TUI workspaces/panels and tools status]] +- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Agents pane defect causes: missing cursors, unscoped runs]] +- runbook: [[packs/paperclip-harness/70-runbooks/editing-agent-settings-and-pausing-resuming-in-tui|Editing agent settings and pausing/resuming in TUI]] +- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Agents pane: scoped runs, cursors, settings editor]] +- session: [[packs/paperclip-harness/80-sessions/rich-discovery-both-defects-fixed-and-the-settings-editor-is|Rich discovery: both-defects-fixed-and-the-settings-editor-is-in 2026-11-10 06:23:51]] +- context: [[packs/paperclip-harness/20-context/both-defects-fixed-and-the-settings-editor-is-in|both-defects-fixed-and-the-settings-editor-is-in]] diff --git a/.pi/memory-vault/packs/paperclip-harness/20-context/paperclip-tui-workspaces-panels-and-tools-status.md b/.pi/memory-vault/packs/paperclip-harness/20-context/paperclip-tui-workspaces-panels-and-tools-status.md new file mode 100644 index 0000000..31fce45 --- /dev/null +++ b/.pi/memory-vault/packs/paperclip-harness/20-context/paperclip-tui-workspaces-panels-and-tools-status.md @@ -0,0 +1,85 @@ +--- +type: context +id: context.paperclip-harness.paperclip-tui-workspaces-panels-and-tools-status +title: Paperclip TUI workspaces/panels and tools status +status: active +source_of_truth: false +freshness: current +last_reviewed: 2026-11-10 +tags: + - pack/paperclip-harness + - agent-memory/context + - paperclip + - tui + - workspaces + - panels + - docs + - ocr +--- + +# Paperclip TUI workspaces/panels and tools status + +Paperclip TUI organizes UI into workspaces containing panels. As of commit `ec7e907` (`docs/WORKSPACES.md`): **22 panels built across 8 workspaces**, with **one panel missing** and **six tool panes** still to wire. + +Remaining work: +1. **Work>review** — the ocr verdict shown beside the diff. This is the only missing panel. +2. **Tool panes** — `t` opens Lazygit, Hunk, Yazi, Neovim, fzf, or ocr in the current workspace; `esc` closes it. The six tools: Lazygit, Hunk, Yazi, Neovim, fzf, ocr. + +`docs/WORKSPACES.md` is the tracking doc for panel/tool-workspace status and should be updated as panels are built. + +## Detailed context from completed turn + +Both defects fixed, and the settings editor is in. + +## The three things you reported + +**Runs were every run in Paperclip.** Correct — 281 company-wide. They are now scoped to this project's tasks, resolved through each run's own task reference, and the task column shows `AUD-166` instead of a bare identifier. `A` toggles between "this project" and "everything in the company", because both are legitimate questions but not the same one. + +**Scrolling instead of selecting.** The run list had no cursor at all — arrows only moved the viewport. There is now one cursor per tab, the selected row is highlighted, and the list scrolls only to keep the cursor visible. `enter` opens that run's log. Tested: picked the second run, its log opened. + +**Roster could not be walked.** Same cause, same fix. The roster is sorted by name so the cursor stays on the same agent after a reload, and `▸` marks the row. + +The log itself also became readable: it is a stream of token deltas, so deltas are gathered back into paragraphs while tool calls, prompts and turn markers become single lines: + +``` +15:55:57 toolcall_start +15:55:57 {"command": "pwd && ls -la && git log --oneline -10 2>/dev/null"} +15:55:58 toolcall_end +15:55:58 assistant: I'll start by inspecting the workspace… +``` + +## Agents>settings is built + +Fourth tab. Role, job title, model, monthly budget, heartbeat. `enter` edits the chosen field inline, `esc` cancels; heartbeat is a toggle. Every save is one `PATCH /api/agents/{id}` that merges into that agent's existing configuration, so a model change does not wipe its working folder or instructions — verified by reading the record back after a save. + +`p`, `P`, `w` pause, resume and wake, acting on the agent chosen on that tab. + +**One thing you should know:** `Builder` was paused at 16:56 with `pauseReason: manual`, from the board key — that was my keystroke test, not you. I have resumed it and it is idle again. Sorry for the scare. + +## Still missing, in order + +1. **Work>review** — the ocr verdict beside the diff. That is now the only missing panel. +2. **Tool panes** — `t` opens Lazygit, Hunk, Yazi, Neovim, fzf or ocr in the current workspace; `esc` closes it. + +`docs/WORKSPACES.md` is updated: 22 panels built across 8 workspaces, one panel missing, six tool panes to wire. Committed as `ec7e907`. + +Which of those two next? + +## Evidence + +- Confidence: 80% +- Reason: Architecture/status details from final summary and commit reference; useful for future work on remaining panels.; enriched with final-answer detail because persistence should be reusable + +## Related + +- [[packs/paperclip-harness/00-system/pi-agent/memory-manifest|Memory Manifest]] + +## Related learned memories + +- context: [[packs/paperclip-harness/20-context/tmp|Paperclip TUI Agents pane]] +- context: [[packs/paperclip-harness/20-context/paperclip-tui-workspaces-panels-and-tools-status|Paperclip TUI workspaces/panels and tools status]] +- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Agents pane defect causes: missing cursors, unscoped runs]] +- runbook: [[packs/paperclip-harness/70-runbooks/editing-agent-settings-and-pausing-resuming-in-tui|Editing agent settings and pausing/resuming in TUI]] +- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Agents pane: scoped runs, cursors, settings editor]] +- session: [[packs/paperclip-harness/80-sessions/rich-discovery-both-defects-fixed-and-the-settings-editor-is|Rich discovery: both-defects-fixed-and-the-settings-editor-is-in 2026-11-10 06:23:51]] +- context: [[packs/paperclip-harness/20-context/both-defects-fixed-and-the-settings-editor-is-in|both-defects-fixed-and-the-settings-editor-is-in]] diff --git a/.pi/memory-vault/packs/paperclip-harness/20-context/tmp.md b/.pi/memory-vault/packs/paperclip-harness/20-context/tmp.md index 9ff6a54..2cb6c17 100644 --- a/.pi/memory-vault/packs/paperclip-harness/20-context/tmp.md +++ b/.pi/memory-vault/packs/paperclip-harness/20-context/tmp.md @@ -466,3 +466,33 @@ Say which of the three you want next, and I will build only that. - session: [[packs/paperclip-harness/80-sessions/rich-discovery-the-full-list-at-the-end-of-the-project-with-|Rich discovery: the-full-list-at-the-end-of-the-project-with-what-exists-tod 2026-11-10 05:59:07]] - context: [[packs/paperclip-harness/20-context/tmp|the-full-list-at-the-end-of-the-project-with-what-exists-tod]] - observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|the-full-list-at-the-end-of-the-project-with-what-exists-tod repository structure and configuration patterns]] + + +--- + +## Update (2026-11-10 06:23:51) + +The Paperclip terminal UI (TUI) has an "Agents" pane with four tabs: roster, runs, log, and settings. It tracks agents (researcher, System, Hub, Chief of Staff, Builder, Documenter), their states (idle/paused), spend vs. budget, and run history. + +Key behaviors: +- **runs tab**: project-scoped to the current project's tasks by default (resolved through each run's task reference, shown as e.g. `AUD-166`). `A` toggles between "this project" and company-wide runs. Company-wide total at time of work: 281 runs. +- **log tab**: renders run logs. Logs are a stream of token deltas; the viewer merges deltas into paragraphs while tool calls, prompts, and turn markers each become single lines (e.g. `toolcall_start`, `toolcall_end`, `assistant:` lines). +- **settings tab**: editable fields for each agent — role, job title, model, monthly budget, heartbeat. `enter` edits a field inline, `esc` cancels, heartbeat is a toggle. Saves go through one `PATCH /api/agents/{id}` that merges into the agent's existing config (does not wipe working folder or instructions). `p`/`P`/`w` pause/resume/wake the selected agent. +- **Navigation**: each tab has its own cursor; `enter` opens detail (e.g. run log). Roster is sorted by name so cursor stays on the same agent after reload; `▸` marks the selected row. + +The pause control writes `pausedAt` and `pauseReason` (e.g. `manual` from a board key) to the agent status. + +## Evidence + +- Confidence: 85% +- Reason: Evidence-backed overview of the Agents pane architecture and behaviors from the turn's tool output and final summary. + +## Related learned memories + +- context: [[packs/paperclip-harness/20-context/tmp|Paperclip TUI Agents pane]] +- context: [[packs/paperclip-harness/20-context/paperclip-tui-workspaces-panels-and-tools-status|Paperclip TUI workspaces/panels and tools status]] +- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Agents pane defect causes: missing cursors, unscoped runs]] +- runbook: [[packs/paperclip-harness/70-runbooks/editing-agent-settings-and-pausing-resuming-in-tui|Editing agent settings and pausing/resuming in TUI]] +- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Agents pane: scoped runs, cursors, settings editor]] +- session: [[packs/paperclip-harness/80-sessions/rich-discovery-both-defects-fixed-and-the-settings-editor-is|Rich discovery: both-defects-fixed-and-the-settings-editor-is-in 2026-11-10 06:23:51]] +- context: [[packs/paperclip-harness/20-context/both-defects-fixed-and-the-settings-editor-is-in|both-defects-fixed-and-the-settings-editor-is-in]] diff --git a/.pi/memory-vault/packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups.md b/.pi/memory-vault/packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups.md index 0a3093e..0fc7597 100644 --- a/.pi/memory-vault/packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups.md +++ b/.pi/memory-vault/packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups.md @@ -280,3 +280,35 @@ Follow-up: user asked to pick item 1..5; only build the chosen item. - session: [[packs/paperclip-harness/80-sessions/paperclip-harness-reverted-pane-split-interface-gap-discover|Paperclip-harness — reverted pane split, interface gap discovery, and PLAN-INTERFACE roadmap]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-home-2026-11-10-05-41-48|Rich discovery: home 2026-11-10 05:41:48]] - context: [[packs/paperclip-harness/20-context/home|home]] + + +--- + +## Update (2026-11-10 06:23:51) + +Delivered and committed (`ec7e907`: "Agents pane: project-scoped runs with selection, roster cursor, editable settings"). + +Changes: +- Runs tab now project-scoped by default with task column, `A` toggles company-wide. +- Added per-tab cursors for runs and roster; `enter` opens the selected run's log; selection highlighted, scroll-follows-cursor. +- Log viewer now merges token deltas into paragraphs; tool calls/prompts/turn markers as single lines. +- New settings tab (4th tab): edit role, job title, model, monthly budget, heartbeat via inline edit (`enter` saves, `esc` cancels, heartbeat toggle). Saves are single `PATCH /api/agents/{id}` merging into existing config; verified config preserved after save. +- `p`/`P`/`w` pause/resume/wake act on the selected agent. +- Updated `docs/WORKSPACES.md` (22 panels across 8 workspaces, 1 missing panel, 6 tool panes to wire). + +Validation: build passed (`build=0`); manual tests of run selection/open, settings edit/save, and pause/resume round-trip. + +## Evidence + +- Confidence: 90% +- Reason: Completed, verified deliverable with commit hash, artifacts, and validation. + +## Related learned memories + +- context: [[packs/paperclip-harness/20-context/tmp|Paperclip TUI Agents pane]] +- context: [[packs/paperclip-harness/20-context/paperclip-tui-workspaces-panels-and-tools-status|Paperclip TUI workspaces/panels and tools status]] +- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Agents pane defect causes: missing cursors, unscoped runs]] +- runbook: [[packs/paperclip-harness/70-runbooks/editing-agent-settings-and-pausing-resuming-in-tui|Editing agent settings and pausing/resuming in TUI]] +- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Agents pane: scoped runs, cursors, settings editor]] +- session: [[packs/paperclip-harness/80-sessions/rich-discovery-both-defects-fixed-and-the-settings-editor-is|Rich discovery: both-defects-fixed-and-the-settings-editor-is-in 2026-11-10 06:23:51]] +- context: [[packs/paperclip-harness/20-context/both-defects-fixed-and-the-settings-editor-is-in|both-defects-fixed-and-the-settings-editor-is-in]] diff --git a/.pi/memory-vault/packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns.md b/.pi/memory-vault/packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns.md index f3d446c..fd0e82d 100644 --- a/.pi/memory-vault/packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns.md +++ b/.pi/memory-vault/packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns.md @@ -979,3 +979,172 @@ Say which of the three you want next, and I will build only that. - session: [[packs/paperclip-harness/80-sessions/rich-discovery-the-full-list-at-the-end-of-the-project-with-|Rich discovery: the-full-list-at-the-end-of-the-project-with-what-exists-tod 2026-11-10 05:59:07]] - context: [[packs/paperclip-harness/20-context/tmp|the-full-list-at-the-end-of-the-project-with-what-exists-tod]] - observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|the-full-list-at-the-end-of-the-project-with-what-exists-tod repository structure and configuration patterns]] + + +--- + +## Update (2026-11-10 06:23:51) + +Three defects were diagnosed in the Paperclip TUI Agents pane: +- **Runs list was company-wide** (281 runs) instead of project-scoped; fixed by resolving each run's task reference to the current project and showing the task ID (e.g. `AUD-166`). Added `A` to toggle project/all scope. +- **Scrolling instead of selecting** (runs tab): the list had no cursor, so arrow keys only moved the viewport. Fix: per-tab cursor, highlighted selection row, scroll only to keep cursor visible. `enter` opens the selected run's log. +- **Roster could not be walked**: same root cause (no cursor); fixed with per-tab cursor and name-sorted roster so selection is stable across reloads. + +Also fixed log readability: raw token-delta stream was gathered into paragraphs while tool calls/prompts/turn markers became single lines. + +## Detailed observations from completed turn + +Both defects fixed, and the settings editor is in. + +## The three things you reported + +**Runs were every run in Paperclip.** Correct — 281 company-wide. They are now scoped to this project's tasks, resolved through each run's own task reference, and the task column shows `AUD-166` instead of a bare identifier. `A` toggles between "this project" and "everything in the company", because both are legitimate questions but not the same one. + +**Scrolling instead of selecting.** The run list had no cursor at all — arrows only moved the viewport. There is now one cursor per tab, the selected row is highlighted, and the list scrolls only to keep the cursor visible. `enter` opens that run's log. Tested: picked the second run, its log opened. + +**Roster could not be walked.** Same cause, same fix. The roster is sorted by name so the cursor stays on the same agent after a reload, and `▸` marks the row. + +The log itself also became readable: it is a stream of token deltas, so deltas are gathered back into paragraphs while tool calls, prompts and turn markers become single lines: + +``` +15:55:57 toolcall_start +15:55:57 {"command": "pwd && ls -la && git log --oneline -10 2>/dev/null"} +15:55:58 toolcall_end +15:55:58 assistant: I'll start by inspecting the workspace… +``` + +## Agents>settings is built + +Fourth tab. Role, job title, model, monthly budget, heartbeat. `enter` edits the chosen field inline, `esc` cancels; heartbeat is a toggle. Every save is one `PATCH /api/agents/{id}` that merges into that agent's existing configuration, so a model change does not wipe its working folder or instructions — verified by reading the record back after a save. + +`p`, `P`, `w` pause, resume and wake, acting on the agent chosen on that tab. + +**One thing you should know:** `Builder` was paused at 16:56 with `pauseReason: manual`, from the board key — that was my keystroke test, not you. I have resumed it and it is idle again. Sorry for the scare. + +## Still missing, in order + +1. **Work>review** — the ocr verdict beside the diff. That is now the only missing panel. +2. **Tool panes** — `t` opens Lazygit, Hunk, Yazi, Neovim, fzf or ocr in the current workspace; `esc` closes it. + +`docs/WORKSPACES.md` is updated: 22 panels built across 8 workspaces, one panel missing, six tool panes to wire. Committed as `ec7e907`. + +Which of those two next? + +## Evidence + +- Confidence: 85% +- Reason: Diagnosis and root-cause findings from tool output and summary; valuable for future Agents pane work.; enriched with final-answer detail because persistence should be reusable + + +--- + +## Update (2026-11-10 06:23:51) + +The Builder agent was observed paused with `status: paused pausedAt: 2026-11-10T05:56:47.518Z reason: manual`, caused by a keystroke test on the board key (not the user). Resuming via the pause/resume control returned HTTP 200 and the agent returned to `status: idle`. + +Caveat for future agents: pause/resume/wake keys (`p`/`P`/`w`) act on the currently selected agent and can unintentionally pause a live agent; always confirm the selection before keying. Pause state persists in agent status and can be resumed via the same control. + +## Detailed observations from completed turn + +Both defects fixed, and the settings editor is in. + +## The three things you reported + +**Runs were every run in Paperclip.** Correct — 281 company-wide. They are now scoped to this project's tasks, resolved through each run's own task reference, and the task column shows `AUD-166` instead of a bare identifier. `A` toggles between "this project" and "everything in the company", because both are legitimate questions but not the same one. + +**Scrolling instead of selecting.** The run list had no cursor at all — arrows only moved the viewport. There is now one cursor per tab, the selected row is highlighted, and the list scrolls only to keep the cursor visible. `enter` opens that run's log. Tested: picked the second run, its log opened. + +**Roster could not be walked.** Same cause, same fix. The roster is sorted by name so the cursor stays on the same agent after a reload, and `▸` marks the row. + +The log itself also became readable: it is a stream of token deltas, so deltas are gathered back into paragraphs while tool calls, prompts and turn markers become single lines: + +``` +15:55:57 toolcall_start +15:55:57 {"command": "pwd && ls -la && git log --oneline -10 2>/dev/null"} +15:55:58 toolcall_end +15:55:58 assistant: I'll start by inspecting the workspace… +``` + +## Agents>settings is built + +Fourth tab. Role, job title, model, monthly budget, heartbeat. `enter` edits the chosen field inline, `esc` cancels; heartbeat is a toggle. Every save is one `PATCH /api/agents/{id}` that merges into that agent's existing configuration, so a model change does not wipe its working folder or instructions — verified by reading the record back after a save. + +`p`, `P`, `w` pause, resume and wake, acting on the agent chosen on that tab. + +**One thing you should know:** `Builder` was paused at 16:56 with `pauseReason: manual`, from the board key — that was my keystroke test, not you. I have resumed it and it is idle again. Sorry for the scare. + +## Still missing, in order + +1. **Work>review** — the ocr verdict beside the diff. That is now the only missing panel. +2. **Tool panes** — `t` opens Lazygit, Hunk, Yazi, Neovim, fzf or ocr in the current workspace; `esc` closes it. + +`docs/WORKSPACES.md` is updated: 22 panels built across 8 workspaces, one panel missing, six tool panes to wire. Committed as `ec7e907`. + +Which of those two next? + +## Evidence + +- Confidence: 80% +- Reason: Concrete observed state, API behavior, and operational caveat.; enriched with final-answer detail because persistence should be reusable + + +--- + +## Update (2026-11-10 06:23:51) + +## Structural/configuration observations (2026-11-10 06:23:51) + +Both defects fixed, and the settings editor is in. + +## The three things you reported + +**Runs were every run in Paperclip.** Correct — 281 company-wide. They are now scoped to this project's tasks, resolved through each run's own task reference, and the task column shows `AUD-166` instead of a bare identifier. `A` toggles between "this project" and "everything in the company", because both are legitimate questions but not the same one. + +**Scrolling instead of selecting.** The run list had no cursor at all — arrows only moved the viewport. There is now one cursor per tab, the selected row is highlighted, and the list scrolls only to keep the cursor visible. `enter` opens that run's log. Tested: picked the second run, its log opened. + +**Roster could not be walked.** Same cause, same fix. The roster is sorted by name so the cursor stays on the same agent after a reload, and `▸` marks the row. + +The log itself also became readable: it is a stream of token deltas, so deltas are gathered back into paragraphs while tool calls, prompts and turn markers become single lines: + +``` +15:55:57 toolcall_start +15:55:57 {"command": "pwd && ls -la && git log --oneline -10 2>/dev/null"} +15:55:58 toolcall_end +15:55:58 assistant: I'll start by inspecting the workspace… +``` + +## Agents>settings is built + +Fourth tab. Role, job title, model, monthly budget, heartbeat. `enter` edits the chosen field inline, `esc` cancels; heartbeat is a toggle. Every save is one `PATCH /api/agents/{id}` that merges into that agent's existing configuration, so a model change does not wipe its working folder or instructions — verified by reading the record back after a save. + +`p`, `P`, `w` pause, resume and wake, acting on the agent chosen on that tab. + +**One thing you should know:** `Builder` was paused at 16:56 with `pauseReason: manual`, from the board key — that was my keystroke test, not you. I have resumed it and it is idle again. Sorry for the scare. + +## Still missing, in order + +1. **Work>review** — the ocr verdict beside the diff. That is now the only missing panel. +2. **Tool panes** — `t` opens Lazygit, Hunk, Yazi, Neovim, fzf or ocr in the current workspace; `esc` closes it. + +`docs/WORKSPACES.md` is updated: 22 panels built across 8 workspaces, one panel missing, six tool panes to wire. Committed as `ec7e907`. + +Which of those two next? + +## Source paths observed + +- `omni/deepseek/deepseek-v4-flash` + +## Evidence + +- Confidence: 82% +- Reason: Captured factual repo structure/configuration patterns from rich discovery + +## Related learned memories + +- context: [[packs/paperclip-harness/20-context/tmp|Paperclip TUI Agents pane]] +- context: [[packs/paperclip-harness/20-context/paperclip-tui-workspaces-panels-and-tools-status|Paperclip TUI workspaces/panels and tools status]] +- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Agents pane defect causes: missing cursors, unscoped runs]] +- runbook: [[packs/paperclip-harness/70-runbooks/editing-agent-settings-and-pausing-resuming-in-tui|Editing agent settings and pausing/resuming in TUI]] +- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Agents pane: scoped runs, cursors, settings editor]] +- session: [[packs/paperclip-harness/80-sessions/rich-discovery-both-defects-fixed-and-the-settings-editor-is|Rich discovery: both-defects-fixed-and-the-settings-editor-is-in 2026-11-10 06:23:51]] +- context: [[packs/paperclip-harness/20-context/both-defects-fixed-and-the-settings-editor-is-in|both-defects-fixed-and-the-settings-editor-is-in]] diff --git a/.pi/memory-vault/packs/paperclip-harness/70-runbooks/editing-agent-settings-and-pausing-resuming-in-tui.md b/.pi/memory-vault/packs/paperclip-harness/70-runbooks/editing-agent-settings-and-pausing-resuming-in-tui.md new file mode 100644 index 0000000..cfcd5b6 --- /dev/null +++ b/.pi/memory-vault/packs/paperclip-harness/70-runbooks/editing-agent-settings-and-pausing-resuming-in-tui.md @@ -0,0 +1,90 @@ +--- +type: runbook +id: runbook.paperclip-harness.editing-agent-settings-and-pausing-resuming-in-tui +title: Editing agent settings and pausing/resuming in TUI +status: active +source_of_truth: false +freshness: current +last_reviewed: 2026-11-10 +tags: + - pack/paperclip-harness + - agent-memory/runbook + - paperclip + - tui + - runbook + - agents + - settings + - pause +--- + +# Editing agent settings and pausing/resuming in TUI + +Trigger: need to change an agent's role/job title/model/budget/heartbeat, or pause/resume/wake an agent via the Paperclip TUI. + +Prerequisites: TUI running, Agents pane available. + +Procedure: +1. Navigate to Agents>settings tab. +2. Move cursor to the target agent on the roster; confirm `▸` is on the intended row (pause keys act on the selected agent — misselection can pause a live agent). +3. `enter` on a field to edit inline; type value; `enter` saves, `esc` cancels. Heartbeat is a toggle. +4. Save issues a single `PATCH /api/agents/{id}` that merges into existing config (does not wipe working folder/instructions). Model change applies to the agent's next run, not an in-flight run. +5. To pause/resume/wake the selected agent press `p` / `P` / `w` respectively. + +Verification / rollback: read the agent record back after saving to confirm fields persisted. If you accidentally pause an agent, resume with `P`; expect resume to return HTTP 200 and status to return to idle. + +## Detailed procedure from completed turn + +Both defects fixed, and the settings editor is in. + +## The three things you reported + +**Runs were every run in Paperclip.** Correct — 281 company-wide. They are now scoped to this project's tasks, resolved through each run's own task reference, and the task column shows `AUD-166` instead of a bare identifier. `A` toggles between "this project" and "everything in the company", because both are legitimate questions but not the same one. + +**Scrolling instead of selecting.** The run list had no cursor at all — arrows only moved the viewport. There is now one cursor per tab, the selected row is highlighted, and the list scrolls only to keep the cursor visible. `enter` opens that run's log. Tested: picked the second run, its log opened. + +**Roster could not be walked.** Same cause, same fix. The roster is sorted by name so the cursor stays on the same agent after a reload, and `▸` marks the row. + +The log itself also became readable: it is a stream of token deltas, so deltas are gathered back into paragraphs while tool calls, prompts and turn markers become single lines: + +``` +15:55:57 toolcall_start +15:55:57 {"command": "pwd && ls -la && git log --oneline -10 2>/dev/null"} +15:55:58 toolcall_end +15:55:58 assistant: I'll start by inspecting the workspace… +``` + +## Agents>settings is built + +Fourth tab. Role, job title, model, monthly budget, heartbeat. `enter` edits the chosen field inline, `esc` cancels; heartbeat is a toggle. Every save is one `PATCH /api/agents/{id}` that merges into that agent's existing configuration, so a model change does not wipe its working folder or instructions — verified by reading the record back after a save. + +`p`, `P`, `w` pause, resume and wake, acting on the agent chosen on that tab. + +**One thing you should know:** `Builder` was paused at 16:56 with `pauseReason: manual`, from the board key — that was my keystroke test, not you. I have resumed it and it is idle again. Sorry for the scare. + +## Still missing, in order + +1. **Work>review** — the ocr verdict beside the diff. That is now the only missing panel. +2. **Tool panes** — `t` opens Lazygit, Hunk, Yazi, Neovim, fzf or ocr in the current workspace; `esc` closes it. + +`docs/WORKSPACES.md` is updated: 22 panels built across 8 workspaces, one panel missing, six tool panes to wire. Committed as `ec7e907`. + +Which of those two next? + +## Evidence + +- Confidence: 80% +- Reason: Repeatable procedure with a safety caveat learned during the turn.; enriched with final-answer detail because persistence should be reusable + +## Related + +- [[packs/paperclip-harness/00-system/pi-agent/memory-manifest|Memory Manifest]] + +## Related learned memories + +- context: [[packs/paperclip-harness/20-context/tmp|Paperclip TUI Agents pane]] +- context: [[packs/paperclip-harness/20-context/paperclip-tui-workspaces-panels-and-tools-status|Paperclip TUI workspaces/panels and tools status]] +- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Agents pane defect causes: missing cursors, unscoped runs]] +- runbook: [[packs/paperclip-harness/70-runbooks/editing-agent-settings-and-pausing-resuming-in-tui|Editing agent settings and pausing/resuming in TUI]] +- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Agents pane: scoped runs, cursors, settings editor]] +- session: [[packs/paperclip-harness/80-sessions/rich-discovery-both-defects-fixed-and-the-settings-editor-is|Rich discovery: both-defects-fixed-and-the-settings-editor-is-in 2026-11-10 06:23:51]] +- context: [[packs/paperclip-harness/20-context/both-defects-fixed-and-the-settings-editor-is-in|both-defects-fixed-and-the-settings-editor-is-in]] diff --git a/.pi/memory-vault/packs/paperclip-harness/80-sessions/rich-discovery-both-defects-fixed-and-the-settings-editor-is.md b/.pi/memory-vault/packs/paperclip-harness/80-sessions/rich-discovery-both-defects-fixed-and-the-settings-editor-is.md new file mode 100644 index 0000000..c19a9bf --- /dev/null +++ b/.pi/memory-vault/packs/paperclip-harness/80-sessions/rich-discovery-both-defects-fixed-and-the-settings-editor-is.md @@ -0,0 +1,81 @@ +--- +type: session +id: session.paperclip-harness.rich-discovery-both-defects-fixed-and-the-settings-editor-is +title: Rich discovery: both-defects-fixed-and-the-settings-editor-is-in 2026-11-10 06:23:51 +status: active +source_of_truth: false +freshness: current +last_reviewed: 2026-11-10 +tags: + - pack/paperclip-harness + - agent-memory/session + - autolearn + - rich-discovery + - repo/both-defects-fixed-and-the-settings-editor-is-in +--- + +# Rich discovery: both-defects-fixed-and-the-settings-editor-is-in 2026-11-10 06:23:51 + +Sanitized rich discovery snapshot captured automatically from the completed turn. + + +## Source paths observed + +- `omni/deepseek/deepseek-v4-flash` + +## Final answer snapshot + +Both defects fixed, and the settings editor is in. + +## The three things you reported + +**Runs were every run in Paperclip.** Correct — 281 company-wide. They are now scoped to this project's tasks, resolved through each run's own task reference, and the task column shows `AUD-166` instead of a bare identifier. `A` toggles between "this project" and "everything in the company", because both are legitimate questions but not the same one. + +**Scrolling instead of selecting.** The run list had no cursor at all — arrows only moved the viewport. There is now one cursor per tab, the selected row is highlighted, and the list scrolls only to keep the cursor visible. `enter` opens that run's log. Tested: picked the second run, its log opened. + +**Roster could not be walked.** Same cause, same fix. The roster is sorted by name so the cursor stays on the same agent after a reload, and `▸` marks the row. + +The log itself also became readable: it is a stream of token deltas, so deltas are gathered back into paragraphs while tool calls, prompts and turn markers become single lines: + +``` +15:55:57 toolcall_start +15:55:57 {"command": "pwd && ls -la && git log --oneline -10 2>/dev/null"} +15:55:58 toolcall_end +15:55:58 assistant: I'll start by inspecting the workspace… +``` + +## Agents>settings is built + +Fourth tab. Role, job title, model, monthly budget, heartbeat. `enter` edits the chosen field inline, `esc` cancels; heartbeat is a toggle. Every save is one `PATCH /api/agents/{id}` that merges into that agent's existing configuration, so a model change does not wipe its working folder or instructions — verified by reading the record back after a save. + +`p`, `P`, `w` pause, resume and wake, acting on the agent chosen on that tab. + +**One thing you should know:** `Builder` was paused at 16:56 with `pauseReason: manual`, from the board key — that was my keystroke test, not you. I have resumed it and it is idle again. Sorry for the scare. + +## Still missing, in order + +1. **Work>review** — the ocr verdict beside the diff. That is now the only missing panel. +2. **Tool panes** — `t` opens Lazygit, Hunk, Yazi, Neovim, fzf or ocr in the current workspace; `esc` closes it. + +`docs/WORKSPACES.md` is updated: 22 panels built across 8 workspaces, one panel missing, six tool panes to wire. Committed as `ec7e907`. + +Which of those two next? + +## Evidence + +- Confidence: 86% +- Reason: Preserve full sanitized discovery detail from a rich investigation turn + +## Related + +- [[packs/paperclip-harness/00-system/pi-agent/memory-manifest|Memory Manifest]] + +## Related learned memories + +- context: [[packs/paperclip-harness/20-context/tmp|Paperclip TUI Agents pane]] +- context: [[packs/paperclip-harness/20-context/paperclip-tui-workspaces-panels-and-tools-status|Paperclip TUI workspaces/panels and tools status]] +- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Agents pane defect causes: missing cursors, unscoped runs]] +- runbook: [[packs/paperclip-harness/70-runbooks/editing-agent-settings-and-pausing-resuming-in-tui|Editing agent settings and pausing/resuming in TUI]] +- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Agents pane: scoped runs, cursors, settings editor]] +- session: [[packs/paperclip-harness/80-sessions/rich-discovery-both-defects-fixed-and-the-settings-editor-is|Rich discovery: both-defects-fixed-and-the-settings-editor-is-in 2026-11-10 06:23:51]] +- context: [[packs/paperclip-harness/20-context/both-defects-fixed-and-the-settings-editor-is-in|both-defects-fixed-and-the-settings-editor-is-in]] diff --git a/README.md b/README.md index 12fadd6..efe5d3d 100644 --- a/README.md +++ b/README.md @@ -59,16 +59,24 @@ program running in the Board workspace. | Workspace>pane | shows | keys | |---|---|---| | Shell>shell | a prompt in the project folder | — | -| Board>board | tasks by group · the chosen task · needs you | ↑↓ j k · enter · a · u · / · tab | -| Work>work | runs · change · facts | [ ] tab · ↑↓ j k · g G | +| Board>board | tasks by group · the chosen task · needs you | ↑↓ j k · enter · a · u · / · t (tools) · tab | +| Work>work | runs · change · facts · review | [ ] tab · ↑↓ j k · g G · t (tools) | | Reply>reply | the task · the thread · the reply box | enter · s · esc | | New task>new | the form · what a task needs · what will be sent | tab · enter · ctrl+n | | Project>project | facts · costs · settings | [ ] tab · ↑↓ j k | | Repair>repair | health · layout · rebuild log | [ ] tab · ↑↓ j k · R · B | +| Agents>agents | roster · runs · log · settings | [ ] tab · ↑↓ j k · enter · p P w · A | Choosing a task on the board and pressing Enter writes it into the project's `.harness/harness.toml`, and every other workspace follows within a second. +### Tool panes + +`t` in `Board>board` or `Work>work` opens a chooser: shell, Lazygit, Hunk, Yazi, Neovim, +fzf, ocr. The tool opens as a pane beside the one you are in, in the same workspace, and +`esc` closes the chooser. Quitting the tool (its own key) closes its pane. Nothing is added +to the layout, so the eight workspaces stay as they are. + ### Refresh and rebuild - **`r`** — read the data again in this panel. Nothing closes, nothing moves, the diff --git a/bin/harness b/bin/harness index be723d5..76b5ec8 100755 Binary files a/bin/harness and b/bin/harness differ diff --git a/docs/WORKSPACES.md b/docs/WORKSPACES.md index e605142..dd21661 100644 --- a/docs/WORKSPACES.md +++ b/docs/WORKSPACES.md @@ -27,7 +27,7 @@ written yet. | `Work>runs` | run timeline and activity, with cost per run | built | | `Work>change` | worktree, branch, base, commits, changed files, the diff | built | | `Work>facts` | task, state, worktree path, branch, base, cost | built | -| `Work>review` | the last ocr verdict for this task: findings, blocking ones in red, and whether a merge would be refused | **missing** | +| `Work>review` | the verdicts recorded on this task: review findings, refused merges, merged results, blocking ones in red | built | ## 4. Reply @@ -78,19 +78,20 @@ Yazi, fzf, Neovim, Lazygit, Hunk and anything else fits without being rebuilt by | Tool | What it is for | Status | |---|---|---| -| Lazygit | branches, commits, staging, history | installed, **not wired** | -| Hunk | reading a change as a review | installed, **not wired** | -| Yazi | files | installed, **not wired** | -| Neovim | editing a file the task touched | installed, **not wired** | -| fzf | picking anything | installed, **not wired** | +| Lazygit | branches, commits, staging, history | wired: `t` in Board>board or Work>work | +| Hunk | reading a change as a review | wired the same way | +| Yazi | files | wired the same way | +| Neovim | editing a file the task touched | wired the same way | +| fzf | picking anything | wired the same way | | shell | any command the interface does not cover | built (`Shell>shell`) | | ocr | the review of the current change | installed, behind `bin/review` | ## Count -- **22 panels built**, across 8 workspaces. -- **1 panel missing**: `Work>review`. -- **6 tool panes** to wire: Lazygit, Hunk, Yazi, Neovim, fzf, ocr. +- **23 panels built**, across 8 workspaces. Nothing is missing from the list above. +- **tool panes wired** in Board>board and Work>work: `t` opens the chooser, `enter` opens + the tool beside the pane, `esc` closes it, and the tool's own quit key closes its pane. + The other workspaces can get the same key whenever it is wanted. Nothing in the missing list changes the layout: each is one more panel inside a pane that already exists, or one pane-opener for a tool. diff --git a/tui/agents.go b/tui/agents.go index c5bb5b9..58843e3 100644 --- a/tui/agents.go +++ b/tui/agents.go @@ -84,7 +84,7 @@ func newAgentsModel(c *paperclip.Client, folder string) agentsModel { } func (m agentsModel) Init() tea.Cmd { - return tea.Batch(m.load(), tickEvery(5*time.Second)) + return tea.Batch(m.load(), tickEvery(5*time.Second), tea.WindowSize()) } func (m agentsModel) load() tea.Cmd { diff --git a/tui/board.go b/tui/board.go index b42c8cb..050e5c2 100644 --- a/tui/board.go +++ b/tui/board.go @@ -49,6 +49,8 @@ type boardModel struct { activity []paperclip.Activity needsTab int + tools bool + toolAt int zones *zone.Manager filter string typing bool @@ -71,7 +73,7 @@ func newBoardModel(c *paperclip.Client, proj paperclip.Project, folder string) b } func (m boardModel) Init() tea.Cmd { - return tea.Batch(m.reload(), tickEvery(5*time.Second)) + return tea.Batch(m.reload(), tickEvery(5*time.Second), tea.WindowSize()) } func (m boardModel) reload() tea.Cmd { @@ -157,6 +159,27 @@ func (m boardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil case tea.KeyMsg: + if m.tools { + switch msg.String() { + case "esc", "q", "t": + m.tools = false + return m, nil + case "up", "k": + m.toolAt = max(0, m.toolAt-1) + return m, nil + case "down", "j": + m.toolAt = min(len(tools)-1, m.toolAt+1) + return m, nil + case "enter": + t := tools[m.toolAt] + m.tools = false + if err := openTool(m.folder, t.name); err != nil { + m.err = err + } + return m, nil + } + return m, nil + } if m.showHelp { m.showHelp = false return m, nil @@ -192,6 +215,9 @@ func (m boardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "?": m.showHelp = true return m, nil + case "t": + m.tools = true + return m, nil case "/": m.typing = true return m, nil @@ -424,6 +450,7 @@ func (m boardModel) footerLine() string { keyHint("enter", "show it everywhere"), keyHint("a", "assign"), keyHint("u", "unblock"), + keyHint("t", "tools"), keyHint("/", "filter"), keyHint("?", "keys"), keyHint("tab", "next panel"), diff --git a/tui/main.go b/tui/main.go index 5d72f7e..c35a64d 100644 --- a/tui/main.go +++ b/tui/main.go @@ -80,11 +80,9 @@ func main() { if dir == "" { dir, _ = os.Getwd() } - issue := *issue - if issue == "" { - issue = watchedTask(dir) - } - m = newWorkModel(client, dir, issue) + // Pass the flag, not a resolved task: an empty flag means "follow the + // task chosen on the Board", and resolving it here would pin the pane. + m = newWorkModel(client, dir, *issue) case "tree": proj, err := client.ResolveProject(*project) diff --git a/tui/project.go b/tui/project.go index 9696860..aca3cec 100644 --- a/tui/project.go +++ b/tui/project.go @@ -60,7 +60,7 @@ func newProjectModel(c *paperclip.Client, folder string) projectModel { } func (m projectModel) Init() tea.Cmd { - return tea.Batch(m.load(), tickEvery(10*time.Second)) + return tea.Batch(m.load(), tickEvery(10*time.Second), tea.WindowSize()) } func (m projectModel) load() tea.Cmd { diff --git a/tui/repair.go b/tui/repair.go index 4f9188e..f128a9e 100644 --- a/tui/repair.go +++ b/tui/repair.go @@ -63,7 +63,7 @@ func newRepairModel(c *paperclip.Client, folder string) repairModel { } func (m repairModel) Init() tea.Cmd { - return tea.Batch(m.load(), tickEvery(8*time.Second)) + return tea.Batch(m.load(), tickEvery(8*time.Second), tea.WindowSize()) } func (m repairModel) load() tea.Cmd { @@ -194,7 +194,7 @@ func (m repairModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil case tickMsg: - return m, tea.Batch(m.load(), tickEvery(8*time.Second)) + return m, tea.Batch(m.load(), tickEvery(8*time.Second), tea.WindowSize()) case tea.KeyMsg: if m.showHelp { diff --git a/tui/reply.go b/tui/reply.go index b853242..ce54553 100644 --- a/tui/reply.go +++ b/tui/reply.go @@ -55,7 +55,7 @@ func newReplyModel(c *paperclip.Client, folder, issueID string) replyModel { } func (m replyModel) Init() tea.Cmd { - return tea.Batch(m.load(), tickEvery(5*time.Second)) + return tea.Batch(m.load(), tickEvery(5*time.Second), tea.WindowSize()) } func (m replyModel) load() tea.Cmd { diff --git a/tui/toolpane.go b/tui/toolpane.go new file mode 100644 index 0000000..41eaee7 --- /dev/null +++ b/tui/toolpane.go @@ -0,0 +1,180 @@ +package main + +// Tool panes: any terminal program this interface does not cover, opened beside +// what you were reading and closed when you are done with it. +// +// Nothing is rebuilt. Lazygit, Hunk, Yazi, Neovim, fzf and ocr are the tools +// people already use, and a pane is the correct place for them: they keep their +// own keys, their own colours and their own way of working. +// +// A tool pane is not part of the layout. It is created on demand in the +// workspace you are in and it disappears when the program exits, so the eight +// workspaces stay as they are. + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "strconv" + "syscall" +) + +type tool struct { + name string + command string + about string +} + +// tools are offered in this order: the shell first because it can reach +// anything, then the three that read code, then the review. +var tools = []tool{ + {"shell", "exec zsh -i", "a prompt in the project folder"}, + {"lazygit", "lazygit", "branches, commits, staging, history"}, + {"hunk", "hunk", "a change read as a review"}, + {"yazi", "yazi", "files"}, + {"nvim", "nvim", "editing one file"}, + {"fzf", "fzf", "picking anything"}, + {"ocr", "ocr review", "the review of the current change, in the terminal"}, +} + +// currentWorkspace finds the workspace this program is drawn in. TUIOS knows; +// the environment names the pane, and the pane's window says which workspace. +func currentWorkspace() (string, string, error) { + session := os.Getenv("TUIOS_SESSION") + pane := os.Getenv("TUIOS_PANE_ID") + if session == "" || pane == "" { + return "", "", fmt.Errorf("this is not running inside a TUIOS pane") + } + out, err := exec.Command("tuios", "list-windows", "-s", session, "--json").Output() + if err != nil { + return "", "", fmt.Errorf("cannot ask TUIOS where this pane is: %w", err) + } + ws := workspaceOf(string(out), pane) + if ws == "" { + return "", "", fmt.Errorf("this pane is not in the layout TUIOS reports") + } + return session, ws, nil +} + +// workspaceOf finds the workspace number of one window in the JSON TUIOS +// prints. It decodes properly rather than scanning text: a pane id appears in +// more than one place, and the first match is not always the window. +func workspaceOf(raw, paneID string) string { + var doc struct { + Windows []struct { + WindowID string `json:"window_id"` + Workspace int `json:"workspace"` + } `json:"windows"` + } + if err := json.Unmarshal([]byte(raw), &doc); err != nil { + return "" + } + for _, w := range doc.Windows { + if w.WindowID == paneID { + return strconv.Itoa(w.Workspace) + } + } + return "" +} + +// openTool starts a program as a new pane in the current workspace. +func openTool(folder, name string) error { + var chosen *tool + for i := range tools { + if tools[i].name == name { + chosen = &tools[i] + } + } + if chosen == nil { + return fmt.Errorf("no tool called %q", name) + } + session, ws, err := currentWorkspace() + if err != nil { + return err + } + log, _ := os.Create(filepath.Join(os.TempDir(), "paperclip-harness-tool.log")) + + cmd := exec.Command("tuios", "new-window", chosen.name, "-s", session, + "--workspace", ws, "--cwd", folder, "--", "/bin/sh", "-c", chosen.command) + cmd.Stdout = log + cmd.Stderr = log + cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true} + if err := cmd.Start(); err != nil { + return fmt.Errorf("cannot open %s: %w", chosen.name, err) + } + go func() { + cmd.Wait() + if log != nil { + log.Close() + } + }() + return nil +} + +// --- the smallest JSON reader that does this one job ------------------------- + +type scanner struct { + s string + pos int +} + +func newScanner(s string) *scanner { return &scanner{s: s} } + +// nextObject returns the next {...} block, or "" at the end. +func (sc *scanner) nextObject() string { + for sc.pos < len(sc.s) && sc.s[sc.pos] != '{' { + sc.pos++ + } + if sc.pos >= len(sc.s) { + return "" + } + depth := 0 + start := sc.pos + for sc.pos < len(sc.s) { + switch sc.s[sc.pos] { + case '{': + depth++ + case '}': + depth-- + if depth == 0 { + sc.pos++ + return sc.s[start:sc.pos] + } + } + sc.pos++ + } + return "" +} + +func contains(s, sub string) bool { + for i := 0; i+len(sub) <= len(s); i++ { + if s[i:i+len(sub)] == sub { + return true + } + } + return false +} + +// fieldNumber reads "name": 12 out of an object. +func fieldNumber(obj, name string) string { + key := `"` + name + `":` + for i := 0; i+len(key) <= len(obj); i++ { + if obj[i:i+len(key)] != key { + continue + } + j := i + len(key) + for j < len(obj) && (obj[j] == ' ' || obj[j] == '"') { + j++ + } + start := j + for j < len(obj) && obj[j] >= '0' && obj[j] <= '9' { + j++ + } + if _, err := strconv.Atoi(obj[start:j]); err == nil { + return obj[start:j] + } + } + return "" +} diff --git a/tui/work.go b/tui/work.go index a6398b1..d0e95de 100644 --- a/tui/work.go +++ b/tui/work.go @@ -44,20 +44,22 @@ type workModel struct { tab int scroll int + tools bool + toolAt int readAt time.Time showHelp bool width, height int } -var workTabs = []string{"runs", "change", "facts"} +var workTabs = []string{"runs", "change", "facts", "review"} func newWorkModel(c *paperclip.Client, folder, issueID string) workModel { return workModel{client: c, folder: folder, issue: issueID, explicit: issueID != ""} } func (m workModel) Init() tea.Cmd { - return tea.Batch(m.load(), tickEvery(5*time.Second)) + return tea.Batch(m.load(), tickEvery(5*time.Second), tea.WindowSize()) } func (m workModel) load() tea.Cmd { @@ -120,14 +122,38 @@ func (m workModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil case tickMsg: - return m, tea.Batch(m.load(), tickEvery(5*time.Second)) + return m, tea.Batch(m.load(), tickEvery(5*time.Second), tea.WindowSize()) case tea.KeyMsg: + if m.tools { + switch msg.String() { + case "esc", "q", "t": + m.tools = false + return m, nil + case "up", "k": + m.toolAt = max(0, m.toolAt-1) + return m, nil + case "down", "j": + m.toolAt = min(len(tools)-1, m.toolAt+1) + return m, nil + case "enter": + t := tools[m.toolAt] + m.tools = false + if err := openTool(m.folder, t.name); err != nil { + m.err = err + } + return m, nil + } + return m, nil + } if m.showHelp { m.showHelp = false return m, nil } switch msg.String() { + case "t": + m.tools = true + return m, nil case "?": m.showHelp = true return m, nil @@ -225,10 +251,24 @@ func (m workModel) View() string { body = m.runsLines(m.width - 4) case 1: body = m.diffLines(m.width - 4) + case 3: + body = m.reviewLines(m.width - 4) default: body = m.factsLines(m.width - 4) } + if m.tools { + entries := make([][2]string, 0, len(tools)) + for i, t := range tools { + mark := " " + if i == m.toolAt { + mark = "▸ " + } + entries = append(entries, [2]string{mark + t.name, t.about}) + } + return helpOverlay("open a tool pane here", entries, m.width, m.height) + } + p := panel{ title: "the work", width: m.width, height: m.height - 2, tabs: workTabs, active: m.tab, focused: true, @@ -249,6 +289,7 @@ func (m workModel) View() string { footer := hintLine([]string{ keyHint("[ ]", "switch tab"), keyHint("↑↓ or j k", "scroll"), + keyHint("t", "open a tool pane"), keyHint("r", "read again"), keyHint("R", "rebuild every pane"), keyHint("q", "leave"), @@ -355,6 +396,45 @@ func (m workModel) diffLines(width int) []string { return out } +// reviewLines shows the verdicts already recorded on the task. A review is +// written as a comment, so the record lives with the work and survives us. +func (m workModel) reviewLines(width int) []string { + out := []string{"", dimStyle().Render(" verdicts recorded on this task")} + if m.task.Identifier == "" { + return append(out, mutedStyle2().Render(" no task chosen")) + } + cs, err := m.client.Comments(m.task.ID) + if err != nil { + return append(out, lipgloss.NewStyle().Foreground(lipgloss.Color(active.Error)).Render(" "+err.Error())) + } + found := 0 + for i := len(cs) - 1; i >= 0 && found < 3; i-- { + body := cs[i].Body + if !strings.HasPrefix(body, "## Review") && + !strings.HasPrefix(body, "## Merge refused") && + !strings.HasPrefix(body, "## Merged") && + !strings.HasPrefix(body, "## Merge undone") { + continue + } + found++ + blocking := strings.Contains(body, "critical") || strings.Contains(body, "**high/") + colour := active.Success + if strings.HasPrefix(body, "## Merge refused") || blocking { + colour = active.Error + } + for _, ln := range wrap(strings.ReplaceAll(body, "\n", " · "), width-2) { + out = append(out, " "+lipgloss.NewStyle().Foreground(lipgloss.Color(colour)).Render(ln)) + } + out = append(out, "") + } + if found == 0 { + out = append(out, mutedStyle2().Render(" no review recorded for this task yet")) + out = append(out, "") + out = append(out, dimStyle().Render(" run one with: bin/review "+m.task.Identifier+" --post")) + } + return out +} + func (m workModel) factsLines(width int) []string { var out []string out = append(out, "")