@agimon-ai/doompi-task 0.0.1-alpha.14 → 0.0.1-alpha.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -12
- package/dist/tui/format.cjs +3 -3
- package/dist/tui/format.cjs.map +1 -1
- package/dist/tui/format.d.cts.map +1 -1
- package/dist/tui/format.d.mts.map +1 -1
- package/dist/tui/format.mjs +3 -3
- package/dist/tui/format.mjs.map +1 -1
- package/dist/tui/selectors.cjs +1 -1
- package/dist/tui/selectors.cjs.map +1 -1
- package/dist/tui/selectors.d.cts +7 -0
- package/dist/tui/selectors.d.cts.map +1 -1
- package/dist/tui/selectors.d.mts +7 -0
- package/dist/tui/selectors.d.mts.map +1 -1
- package/dist/tui/selectors.mjs +1 -1
- package/dist/tui/selectors.mjs.map +1 -1
- package/package.json +16 -9
package/README.md
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# @agimon-ai/doompi-task
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**A task list that survives the conversation.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
This is a file-backed task graph, not a checklist copied into chat. Tasks have statuses and
|
|
6
|
+
dependencies, can be handed to subagents, and remain authoritative after compaction rewrites
|
|
7
|
+
the transcript. The agent can forget the prose; it does not get to forget the work.
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
This is part of [Doompi](https://www.npmjs.com/package/@agimon-ai/doompi). Most users get
|
|
10
|
+
it with the distribution; install it directly when embedding the task graph in another Pi
|
|
11
|
+
setup.
|
|
11
12
|
|
|
12
13
|
## Install
|
|
13
14
|
|
|
@@ -17,18 +18,26 @@ npm install @agimon-ai/doompi-task
|
|
|
17
18
|
|
|
18
19
|
## How it loads
|
|
19
20
|
|
|
20
|
-
Doompi loads
|
|
21
|
-
|
|
21
|
+
Doompi loads tasks as core. It is not a layer and does not appear in `.doom/modes.yaml`.
|
|
22
|
+
The bare package name follows its Pi manifest and selects the Doom adapter. That adapter
|
|
23
|
+
connects the graph to the Doom TUI and team runtime.
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
## What it does
|
|
26
|
+
|
|
27
|
+
- Stores tasks on disk instead of relying on transcript memory.
|
|
28
|
+
- Enforces dependency and lifecycle transitions.
|
|
29
|
+
- Shares one board with Doompi subagents.
|
|
30
|
+
- Keeps delegation visible to the parent session.
|
|
31
|
+
- Gives compaction an authoritative task snapshot to carry forward.
|
|
24
32
|
|
|
25
33
|
## Leader
|
|
26
34
|
|
|
27
|
-
`SPC t t` opens
|
|
35
|
+
`SPC t t` opens the task board.
|
|
28
36
|
|
|
29
|
-
##
|
|
37
|
+
## Public API
|
|
30
38
|
|
|
31
|
-
|
|
39
|
+
The package exports its task model, storage, tools, TUI, and Pi/Doom adapters through
|
|
40
|
+
supported subpaths. See `package.json` for the complete list.
|
|
32
41
|
|
|
33
42
|
## License
|
|
34
43
|
|
package/dist/tui/format.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../services/delegation/manager.cjs");let t=require("@earendil-works/pi-tui"),n=require("@agimon-ai/doompi-ui/theme"),r=require("@agimon-ai/doompi-ui/toolChrome");const i={pending:`○`,in_progress:`◐`,completed:`●`,failed:`✗`,deleted:`⊘`},a={pending:`dim`,in_progress:`warning`,completed:`success`,failed:`error`,deleted:`muted`},o={pending:`pending`,in_progress:`in progress`,completed:`completed`,failed:`failed`,deleted:`deleted`},s={...i,completed:`✓`,deleted:i.failed},c={...a,deleted:`error`},l=1e3,u=/^working:\s*/i;function
|
|
2
|
-
`).filter(e=>e.trim().length>0)}function
|
|
3
|
-
`),0,0)}if(!i)return new t.Text(r.fg(`success`,`✓`),0,0);let s=e=>({left:
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../services/delegation/manager.cjs");let t=require("@earendil-works/pi-tui"),n=require("@agimon-ai/doompi-ui/theme"),r=require("@agimon-ai/doompi-ui/toolChrome");const i={pending:`○`,in_progress:`◐`,completed:`●`,failed:`✗`,deleted:`⊘`},a={pending:`dim`,in_progress:`warning`,completed:`success`,failed:`error`,deleted:`muted`},o={pending:`pending`,in_progress:`in progress`,completed:`completed`,failed:`failed`,deleted:`deleted`},s={...i,completed:`✓`,deleted:i.failed},c={...a,deleted:`error`},l=1e3,u=/^working:\s*/i,d=/\s+/gu;function f(e){return e.replace(d,` `).trim()}function p(e){if(e)return f(e.replace(u,``))||void 0}const m={upsert:`✎`,delete:`×`,get:`›`,list:`☰`,clear:`∅`,assign:`⇒`,cancel:`⊗`};function h(e){return e.length===0?m.upsert:e.every(e=>e.id===void 0)?`+`:e.every(e=>e.id!==void 0)?`→`:m.upsert}function g(e,t){return t.fg(c[e],s[e])}function _(e){let t=Math.max(0,Math.floor(e/1e3)),n=t%60,r=Math.floor(t/60);if(r<60)return r===0?`${n}s`:`${r}m${String(n).padStart(2,`0`)}s`;let i=r%60;return`${Math.floor(r/60)}h${String(i).padStart(2,`0`)}m${String(n).padStart(2,`0`)}s`}function v(e){let t=Math.max(0,Math.round(e));return t<l?String(t):t<1e4?`${(t/l).toFixed(1)}k`:`${Math.round(t/l)}k`}function y(t,r,i,a,o=Date.now(),s){let c=t.status===`in_progress`?`accent`:t.status===`completed`||t.status===`deleted`?`muted`:`text`,l=r.fg(c,f(t.subject));(t.status===`completed`||t.status===`deleted`)&&(l=r.strikethrough(l));let u=g(t.status,r);i&&(u+=` ${r.fg(`dim`,`#${t.id}`)}`),u+=` ${l}`;let d=t.delegation?.agent,m=d?f(d):void 0;if(m&&t.delegation?.state===`running`){let i=[];a&&(i.push(r.fg(`muted`,`[${_(e.DelegationManager.elapsedMs(a,o))}]`)),a.tokens!==void 0&&i.push(r.fg(`muted`,`[${v(a.tokens)}]`)));let c=t.delegation.runId??t.delegation.requestId;i.push(r.fg(s??(0,n.agentIdentityColor)(c),`[${m}]`)),u+=` ${i.join(``)}`;let l=p(a?.currentTool);l&&(u+=` ${r.fg(`muted`,`· ${l}`)}`)}else m&&(u+=` ${r.fg(`muted`,`[${m}]`)}`);return t.status===`in_progress`&&t.activeForm&&(u+=` ${r.fg(`muted`,`(${f(t.activeForm)})`)}`),t.blockedBy?.length&&(u+=` ${r.fg(`muted`,`⛓ ${t.blockedBy.map(e=>`#${e}`).join(`,`)}`)}`),u}function b(e,t){let n=e.status===`in_progress`&&e.activeForm?` (${e.activeForm})`:``,r=e.delegation?.agent?` [${e.delegation.agent}]`:``,i=e.blockedBy?.length?` ⛓ ${e.blockedBy.map(e=>`#${e}`).join(`,`)}`:``;return` ${t} #${e.id} ${e.subject}${n}${r}${i}`}function x(e,t,n){let i=e.action===`upsert`?e.tasks??[]:[],a=e.action===`upsert`?h(i):m[e.action]??e.action,s=(0,r.renderToolHeading)(`task`,`${a} ${e.action}`,t);if(e.action===`upsert`){if(i.length===1){let e=i[0];if(e.id===void 0)e.subject&&(s+=` ${t.fg(`dim`,e.subject)}`);else{let r=n.find(t=>t.id===e.id)?.subject??e.subject??`#${e.id}`;s+=` ${t.fg(`accent`,r)}`}}else i.length>1&&(s+=` ${t.fg(`dim`,`${i.length} tasks`)}`)}else if(e.action===`assign`){let r=e.assignments??[];if(r.length===1){let e=r[0],i=n.find(t=>t.id===e.id)?.subject??`#${e.id}`;s+=` ${t.fg(`accent`,i)}`,e.agent&&(s+=` ${t.fg(`muted`,`→ ${e.agent}`)}`)}else r.length>1&&(s+=` ${t.fg(`dim`,`${r.length} tasks`)}`)}else if((e.action===`get`||e.action===`delete`||e.action===`cancel`)&&e.id!==void 0){let r=n.find(t=>t.id===e.id)?.subject??`#${e.id}`;s+=` ${t.fg(`accent`,r)}`}else e.action===`list`&&e.status&&(s+=` ${t.fg(`muted`,o[e.status])}`);return new r.DoomToolCall(s)}var S=class{rows;constructor(e){this.rows=e}invalidate(){}render(e){return this.rows.map(({left:n,right:r})=>{if(r===void 0||r.length===0)return(0,t.truncateToWidth)(n,e);let i=(0,t.visibleWidth)(r),a=e-i-2;if(a<=0)return(0,t.truncateToWidth)(n,e);let o=(0,t.truncateToWidth)(n,a),s=Math.max(2,e-(0,t.visibleWidth)(o)-i);return o+` `.repeat(s)+r})}},C=class{content;theme;constructor(e,t){this.content=e,this.theme=t}render(e){if(e<=0)return[];if(e<3)return this.content.render(e);let t=e-2,n=this.theme.fg(`borderMuted`,`─`.repeat(t)),r=this.content.render(t).map(e=>` ${e}`);return[` ${n}`,...r,``]}invalidate(){this.content.invalidate()}};function w(e){return(e.content??[]).map(e=>e.text??``).join(``)}function T(e){return e.split(`
|
|
2
|
+
`).filter(e=>e.trim().length>0)}function E(e){let t=e.params,n=e.tasks;return t.includeDeleted!==!0&&(n=n.filter(e=>e.status!==`deleted`)),t.status&&(n=n.filter(e=>e.status===t.status)),n}function D(e){let t=e.params;if(t.id!==void 0)return e.tasks.find(e=>e.id===t.id)}function O(e){return(e.upsert?.applied??[]).flatMap(t=>e.tasks.find(e=>e.id===t)??[])}function k(e){return(e.assignment?.assigned??[]).flatMap(t=>e.tasks.find(e=>e.id===t)??[])}function A(e,n,r){let i=e.details;if(n.isPartial){let n=T(w(e)).at(-1)??`working`;return new t.Text(r.fg(`warning`,`◐`)+r.fg(`dim`,` ${n}`),0,0)}if(e.isError===!0||i?.error!==void 0){let a=T(i?.error??w(e));if(a.length<=1)return new t.Text(r.fg(`error`,`✗`)+r.fg(`dim`,a[0]?` ${a[0]}`:``),0,0);let o=n.expanded?a:a.slice(0,8);return new t.Text([...o.map(e=>r.fg(`toolOutput`,e)),r.fg(`error`,`✗`)].join(`
|
|
3
|
+
`),0,0)}if(!i)return new t.Text(r.fg(`success`,`✓`),0,0);let s=e=>({left:y(e,r,!0),right:r.fg(a[e.status],o[e.status])});if(i.action===`list`){let e=E(i),t=n.expanded?e:e.slice(0,8),a=t.map(s),o=e.length===0?`no tasks`:`${e.length} task${e.length===1?``:`s`}`;return e.length>t.length&&(o+=` · ctrl+o`),a.push({left:r.fg(`success`,`✓`)+r.fg(`dim`,` ${o}`)}),new S(a)}if(i.action===`clear`)return new t.Text(r.fg(`success`,`✓`)+r.fg(`dim`,` cleared`),0,0);if(i.action===`assign`&&i.assignment){let e=k(i),t=i.assignment.failed;if(e.length===1&&t===0)return new S([s(e[0])]);let a=n.expanded?e:e.slice(0,8),o=a.map(s),c=`${e.length} assigned`;t>0&&(c+=` · ${t} failed`),e.length>a.length&&(c+=` · ctrl+o`);let l=t>0?r.fg(`warning`,`!`):r.fg(`success`,`✓`);return o.push({left:l+r.fg(`dim`,` ${c}`)}),new S(o)}if(i.action===`upsert`){let e=O(i),t=i.upsert?.failed??0;if(e.length===1&&t===0)return new S([s(e[0])]);let a=n.expanded?e:e.slice(0,8),o=a.map(s),c=`${e.length} applied`;t>0&&(c+=` · ${t} failed`),e.length>a.length&&(c+=` · ctrl+o`);let l=t>0?r.fg(`warning`,`!`):r.fg(`success`,`✓`);return o.push({left:l+r.fg(`dim`,` ${c}`)}),new S(o)}let c=D(i);return c?new S([s(c)]):new t.Text(r.fg(`success`,`✓`),0,0)}function j(e,t,n){return new C(A(e,t,n),n)}exports.ACTION_GLYPH=m,exports.JustifiedRows=S,exports.STATUS_COLOR=a,exports.STATUS_GLYPH=i,exports.STATUS_LABEL=o,exports.formatCommandTaskLine=b,exports.formatElapsedMs=_,exports.formatOverlayTaskLine=y,exports.formatTokenCount=v,exports.overlayStatusGlyph=g,exports.renderTaskCall=x,exports.renderTaskResult=j;
|
|
4
4
|
//# sourceMappingURL=format.cjs.map
|
package/dist/tui/format.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.cjs","names":["DelegationManager","agentIdentityColor","renderToolHeading","DoomToolCall","truncateToWidth","visibleWidth","Text"],"sources":["../../src/tui/format.ts"],"sourcesContent":["import { agentIdentityColor } from '@agimon-ai/doompi-ui/theme';\nimport { DoomToolCall, renderToolHeading } from '@agimon-ai/doompi-ui/toolChrome';\nimport type { Theme } from '@earendil-works/pi-coding-agent';\nimport { type Component, Text, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\nimport { DelegationManager, type DelegationProgress } from '../services/delegation/manager.ts';\nimport type {\n Task,\n TaskAction,\n TaskDetails,\n TaskItemMutation,\n TaskMutationParams,\n TaskStatus,\n} from '../services/store/types.ts';\n\nexport const STATUS_GLYPH: Record<TaskStatus, string> = {\n pending: '○',\n in_progress: '◐',\n completed: '●',\n failed: '✗',\n deleted: '⊘',\n};\n\ntype TaskStatusColor = 'dim' | 'warning' | 'success' | 'error' | 'muted';\n\nexport const STATUS_COLOR: Record<TaskStatus, TaskStatusColor> = {\n pending: 'dim',\n in_progress: 'warning',\n completed: 'success',\n failed: 'error',\n deleted: 'muted',\n};\n\nexport const STATUS_LABEL: Record<TaskStatus, string> = {\n pending: 'pending',\n in_progress: 'in progress',\n completed: 'completed',\n failed: 'failed',\n deleted: 'deleted',\n};\n\nconst OVERLAY_STATUS_GLYPH: Record<TaskStatus, string> = {\n ...STATUS_GLYPH,\n completed: '✓',\n deleted: STATUS_GLYPH.failed,\n};\n\nconst OVERLAY_STATUS_COLOR: Record<TaskStatus, TaskStatusColor> = {\n ...STATUS_COLOR,\n deleted: 'error',\n};\nconst MILLISECONDS_PER_SECOND = 1000;\nconst SECONDS_PER_MINUTE = 60;\nconst MINUTES_PER_HOUR = 60;\nconst TOKENS_PER_KILO = 1000;\nconst TOKEN_DECIMAL_LIMIT = 10_000;\nconst REDUNDANT_WORKING_PREFIX = /^working:\\s*/i;\n\n/** Thinking text already communicates activity; keep only its useful content. */\nfunction progressText(currentTool: string | undefined): string | undefined {\n const text = currentTool?.replace(REDUNDANT_WORKING_PREFIX, '').trim();\n return text || undefined;\n}\n\nexport const ACTION_GLYPH: Record<TaskAction, string> = {\n upsert: '✎',\n delete: '×',\n get: '›',\n list: '☰',\n clear: '∅',\n assign: '⇒',\n cancel: '⊗',\n};\n\n/**\n * `upsert` is two operations behind one name, so a homogeneous batch narrows to\n * the glyph the operator already reads: `+` means new work appeared, `→` means\n * work moved. A mixed batch keeps the neutral write glyph.\n */\nconst UPSERT_CREATE_GLYPH = '+';\nconst UPSERT_UPDATE_GLYPH = '→';\n\nfunction upsertGlyph(items: readonly TaskItemMutation[]): string {\n if (items.length === 0) return ACTION_GLYPH.upsert;\n if (items.every((item) => item.id === undefined)) return UPSERT_CREATE_GLYPH;\n if (items.every((item) => item.id !== undefined)) return UPSERT_UPDATE_GLYPH;\n return ACTION_GLYPH.upsert;\n}\n\nexport function overlayStatusGlyph(status: TaskStatus, theme: Theme): string {\n return theme.fg(OVERLAY_STATUS_COLOR[status], OVERLAY_STATUS_GLYPH[status]);\n}\n\nexport function formatElapsedMs(durationMs: number): string {\n const totalSeconds = Math.max(0, Math.floor(durationMs / MILLISECONDS_PER_SECOND));\n const seconds = totalSeconds % SECONDS_PER_MINUTE;\n const totalMinutes = Math.floor(totalSeconds / SECONDS_PER_MINUTE);\n if (totalMinutes < MINUTES_PER_HOUR) {\n return totalMinutes === 0 ? `${seconds}s` : `${totalMinutes}m${String(seconds).padStart(2, '0')}s`;\n }\n const minutes = totalMinutes % MINUTES_PER_HOUR;\n const hours = Math.floor(totalMinutes / MINUTES_PER_HOUR);\n return `${hours}h${String(minutes).padStart(2, '0')}m${String(seconds).padStart(2, '0')}s`;\n}\n\nexport function formatTokenCount(tokens: number): string {\n const count = Math.max(0, Math.round(tokens));\n if (count < TOKENS_PER_KILO) return String(count);\n if (count < TOKEN_DECIMAL_LIMIT) return `${(count / TOKENS_PER_KILO).toFixed(1)}k`;\n return `${Math.round(count / TOKENS_PER_KILO)}k`;\n}\n\n/**\n * One task row for the persistent overlay.\n *\n * A delegated row carries the agent name and its current tool so the operator\n * can see that background work is progressing without opening a transcript.\n */\nexport function formatOverlayTaskLine(\n task: Task,\n theme: Theme,\n showId: boolean,\n progress?: DelegationProgress,\n nowMs = Date.now(),\n agentColor?: Parameters<Theme['fg']>[0],\n): string {\n const subjectColor =\n task.status === 'in_progress'\n ? 'accent'\n : task.status === 'completed' || task.status === 'deleted'\n ? 'muted'\n : 'text';\n let subject = theme.fg(subjectColor, task.subject);\n if (task.status === 'completed' || task.status === 'deleted') {\n subject = theme.strikethrough(subject);\n }\n\n let line = overlayStatusGlyph(task.status, theme);\n if (showId) line += ` ${theme.fg('dim', `#${task.id}`)}`;\n line += ` ${subject}`;\n\n const agent = task.delegation?.agent;\n if (agent && task.delegation?.state === 'running') {\n // No progress means no timing is known, so the chips are omitted rather than\n // reported as `[0s]`. The transcript renderer has no progress feed at all.\n const chips: string[] = [];\n if (progress) {\n chips.push(theme.fg('muted', `[${formatElapsedMs(DelegationManager.elapsedMs(progress, nowMs))}]`));\n if (progress.tokens !== undefined) chips.push(theme.fg('muted', `[${formatTokenCount(progress.tokens)}]`));\n }\n const identity = task.delegation.runId ?? task.delegation.requestId;\n chips.push(theme.fg(agentColor ?? agentIdentityColor(identity), `[${agent}]`));\n line += ` ${chips.join('')}`;\n const activity = progressText(progress?.currentTool);\n if (activity) line += ` ${theme.fg('muted', `· ${activity}`)}`;\n } else if (agent) {\n line += ` ${theme.fg('muted', `[${agent}]`)}`;\n }\n\n if (task.status === 'in_progress' && task.activeForm) {\n line += ` ${theme.fg('muted', `(${task.activeForm})`)}`;\n }\n if (task.blockedBy?.length) {\n line += ` ${theme.fg('muted', `⛓ ${task.blockedBy.map((id) => `#${id}`).join(',')}`)}`;\n }\n return line;\n}\n\n/** One task line for the `/tasks` command output. */\nexport function formatCommandTaskLine(task: Task, glyph: string): string {\n const form = task.status === 'in_progress' && task.activeForm ? ` (${task.activeForm})` : '';\n const agent = task.delegation?.agent ? ` [${task.delegation.agent}]` : '';\n const block = task.blockedBy?.length ? ` ⛓ ${task.blockedBy.map((id) => `#${id}`).join(',')}` : '';\n return ` ${glyph} #${task.id} ${task.subject}${form}${agent}${block}`;\n}\n\nexport function renderTaskCall(\n args: TaskMutationParams & { action: TaskAction },\n theme: Theme,\n tasks: readonly Task[],\n): Component {\n const items = args.action === 'upsert' ? (args.tasks ?? []) : [];\n const glyph = args.action === 'upsert' ? upsertGlyph(items) : (ACTION_GLYPH[args.action] ?? args.action);\n let text = renderToolHeading('task', `${glyph} ${args.action}`, theme);\n\n if (args.action === 'upsert') {\n // One entry reads exactly as create or update did; a batch reads as a count\n // rather than a subject it would have to pick arbitrarily.\n if (items.length === 1) {\n const only = items[0];\n if (only.id === undefined) {\n if (only.subject) text += ` ${theme.fg('dim', only.subject)}`;\n } else {\n const subject = tasks.find((task) => task.id === only.id)?.subject ?? only.subject ?? `#${only.id}`;\n text += ` ${theme.fg('accent', subject)}`;\n }\n } else if (items.length > 1) {\n text += ` ${theme.fg('dim', `${items.length} tasks`)}`;\n }\n } else if (args.action === 'assign') {\n const assignments = args.assignments ?? [];\n if (assignments.length === 1) {\n const only = assignments[0];\n const subject = tasks.find((task) => task.id === only.id)?.subject ?? `#${only.id}`;\n text += ` ${theme.fg('accent', subject)}`;\n if (only.agent) text += ` ${theme.fg('muted', `→ ${only.agent}`)}`;\n } else if (assignments.length > 1) {\n text += ` ${theme.fg('dim', `${assignments.length} tasks`)}`;\n }\n } else if ((args.action === 'get' || args.action === 'delete' || args.action === 'cancel') && args.id !== undefined) {\n const subject = tasks.find((task) => task.id === args.id)?.subject ?? `#${args.id}`;\n text += ` ${theme.fg('accent', subject)}`;\n } else if (args.action === 'list' && args.status) {\n text += ` ${theme.fg('muted', STATUS_LABEL[args.status])}`;\n }\n return new DoomToolCall(text);\n}\n\n/** Task rows kept in a collapsed result, before `ctrl+o` reveals the rest. */\nconst TRANSCRIPT_TASK_ROWS = 8;\n/** Minimum gap between a row's subject and its right-aligned status. */\nconst STATUS_GUTTER = 2;\n\nexport interface JustifiedRow {\n left: string;\n /** Pinned to the right edge. The subject is truncated before this is dropped. */\n right?: string;\n}\n\n/**\n * Rows whose trailing text is flushed right.\n *\n * `Text` cannot do this: the status has to be placed against the viewport edge,\n * which is only known at render time.\n */\nexport class JustifiedRows implements Component {\n constructor(private readonly rows: readonly JustifiedRow[]) {}\n\n /**\n * Nothing to discard: the rows are themed by the caller and held verbatim, and\n * Pi rebuilds this component from `renderResult` whenever the theme changes.\n */\n invalidate(): void {}\n\n render(width: number): string[] {\n return this.rows.map(({ left, right }) => {\n if (right === undefined || right.length === 0) return truncateToWidth(left, width);\n const rightWidth = visibleWidth(right);\n const room = width - rightWidth - STATUS_GUTTER;\n if (room <= 0) return truncateToWidth(left, width);\n const subject = truncateToWidth(left, room);\n const gap = Math.max(STATUS_GUTTER, width - visibleWidth(subject) - rightWidth);\n return subject + ' '.repeat(gap) + right;\n });\n }\n}\n\n/**\n * Self-owned task shell with the same structural divider as the bash tool.\n *\n * Task rows already carry precise per-task state, so the divider stays neutral\n * instead of competing with or appearing to animate between those states.\n */\nclass TaskResultFrame implements Component {\n constructor(\n private readonly content: Component,\n private readonly theme: Theme,\n ) {}\n\n render(width: number): string[] {\n if (width <= 0) return [];\n if (width < 3) return this.content.render(width);\n\n const contentWidth = width - 2;\n const divider = this.theme.fg('borderMuted', '─'.repeat(contentWidth));\n const lines = this.content.render(contentWidth).map((line) => ` ${line}`);\n return [` ${divider}`, ...lines, ''];\n }\n\n invalidate(): void {\n this.content.invalidate();\n }\n}\n\nexport interface TaskResultLike {\n content?: Array<{ type: string; text?: string }>;\n details?: unknown;\n isError?: boolean;\n}\n\nexport interface TaskResultOptions {\n expanded: boolean;\n isPartial: boolean;\n}\n\n/** Concatenated result text, which is all a thrown failure leaves behind. */\nfunction contentText(result: TaskResultLike): string {\n return (result.content ?? []).map((block) => block.text ?? '').join('');\n}\n\nfunction nonEmptyLines(text: string): string[] {\n return text.split('\\n').filter((line) => line.trim().length > 0);\n}\n\n/**\n * The rows `list` reported to the model.\n *\n * `details.tasks` is the whole post-mutation document, so the filters applied by\n * `formatContent` have to be applied again here or the transcript would disagree\n * with the text the model was given.\n */\nfunction listedTasks(details: TaskDetails): Task[] {\n const params = details.params as TaskMutationParams;\n let view = details.tasks;\n if (params.includeDeleted !== true) view = view.filter((task) => task.status !== 'deleted');\n if (params.status) view = view.filter((task) => task.status === params.status);\n return view;\n}\n\n/** The single task an id-bearing action acted on, so the transcript names it rather than a bare tick. */\nfunction subjectTask(details: TaskDetails): Task | undefined {\n const params = details.params as TaskMutationParams;\n if (params.id === undefined) return undefined;\n return details.tasks.find((task) => task.id === params.id);\n}\n\n/**\n * Tasks an upsert applied, resolved from the ids the reducer reported.\n *\n * The ids are read rather than guessed from the tail of the list: a batch\n * touches many rows, and a failed entry contributes none.\n */\nfunction upsertTasks(details: TaskDetails): Task[] {\n return (details.upsert?.applied ?? []).flatMap((id) => details.tasks.find((task) => task.id === id) ?? []);\n}\n\n/** Tasks successfully handed off by a native assignment batch. */\nfunction assignedTasks(details: TaskDetails): Task[] {\n return (details.assignment?.assigned ?? []).flatMap((id) => details.tasks.find((task) => task.id === id) ?? []);\n}\n\nfunction renderTaskResultBody(result: TaskResultLike, options: TaskResultOptions, theme: Theme): Component {\n const details = result.details as TaskDetails | undefined;\n\n // Running. `assign` and `cancel` stream a placeholder before the run settles.\n if (options.isPartial) {\n const text = nonEmptyLines(contentText(result)).at(-1) ?? 'working';\n return new Text(theme.fg('warning', '◐') + theme.fg('dim', ` ${text}`), 0, 0);\n }\n\n // Failed. Every failure path throws, so Pi hands back error text and no\n // details; without this the block would render a green tick on a failure.\n if (result.isError === true || details?.error !== undefined) {\n const message = details?.error ?? contentText(result);\n const all = nonEmptyLines(message);\n if (all.length <= 1) {\n return new Text(theme.fg('error', '✗') + theme.fg('dim', all[0] ? ` ${all[0]}` : ''), 0, 0);\n }\n const body = options.expanded ? all : all.slice(0, TRANSCRIPT_TASK_ROWS);\n return new Text([...body.map((line) => theme.fg('toolOutput', line)), theme.fg('error', '✗')].join('\\n'), 0, 0);\n }\n\n if (!details) return new Text(theme.fg('success', '✓'), 0, 0);\n\n /** Status rides on the task's own row, flushed right, rather than costing a line. */\n const row = (task: Task): JustifiedRow => ({\n left: formatOverlayTaskLine(task, theme, true),\n right: theme.fg(STATUS_COLOR[task.status], STATUS_LABEL[task.status]),\n });\n\n if (details.action === 'list') {\n const tasks = listedTasks(details);\n const shown = options.expanded ? tasks : tasks.slice(0, TRANSCRIPT_TASK_ROWS);\n const rows: JustifiedRow[] = shown.map(row);\n let summary = tasks.length === 0 ? 'no tasks' : `${tasks.length} task${tasks.length === 1 ? '' : 's'}`;\n if (tasks.length > shown.length) summary += ' · ctrl+o';\n rows.push({ left: theme.fg('success', '✓') + theme.fg('dim', ` ${summary}`) });\n return new JustifiedRows(rows);\n }\n\n if (details.action === 'clear') {\n return new Text(theme.fg('success', '✓') + theme.fg('dim', ' cleared'), 0, 0);\n }\n\n if (details.action === 'assign' && details.assignment) {\n const assigned = assignedTasks(details);\n const failed = details.assignment.failed;\n if (assigned.length === 1 && failed === 0) return new JustifiedRows([row(assigned[0])]);\n\n const shown = options.expanded ? assigned : assigned.slice(0, TRANSCRIPT_TASK_ROWS);\n const rows: JustifiedRow[] = shown.map(row);\n let summary = `${assigned.length} assigned`;\n if (failed > 0) summary += ` · ${failed} failed`;\n if (assigned.length > shown.length) summary += ' · ctrl+o';\n const mark = failed > 0 ? theme.fg('warning', '!') : theme.fg('success', '✓');\n rows.push({ left: mark + theme.fg('dim', ` ${summary}`) });\n return new JustifiedRows(rows);\n }\n\n if (details.action === 'upsert') {\n const touched = upsertTasks(details);\n const failed = details.upsert?.failed ?? 0;\n // One applied task and nothing failed is the common case: keep the single\n // row it has always had rather than dressing it as a bulk report.\n if (touched.length === 1 && failed === 0) return new JustifiedRows([row(touched[0])]);\n\n const shown = options.expanded ? touched : touched.slice(0, TRANSCRIPT_TASK_ROWS);\n const rows: JustifiedRow[] = shown.map(row);\n let summary = `${touched.length} applied`;\n if (failed > 0) summary += ` · ${failed} failed`;\n if (touched.length > shown.length) summary += ' · ctrl+o';\n // Failed entries get no row: there is no task to render, and their messages\n // are already in the text the model was given.\n const mark = failed > 0 ? theme.fg('warning', '!') : theme.fg('success', '✓');\n rows.push({ left: mark + theme.fg('dim', ` ${summary}`) });\n return new JustifiedRows(rows);\n }\n\n const task = subjectTask(details);\n if (!task) return new Text(theme.fg('success', '✓'), 0, 0);\n return new JustifiedRows([row(task)]);\n}\n\nexport function renderTaskResult(result: TaskResultLike, options: TaskResultOptions, theme: Theme): Component {\n return new TaskResultFrame(renderTaskResultBody(result, options, theme), theme);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"format.cjs","names":["DelegationManager","agentIdentityColor","renderToolHeading","DoomToolCall","truncateToWidth","visibleWidth","Text"],"sources":["../../src/tui/format.ts"],"sourcesContent":["import { agentIdentityColor } from '@agimon-ai/doompi-ui/theme';\nimport { DoomToolCall, renderToolHeading } from '@agimon-ai/doompi-ui/toolChrome';\nimport type { Theme } from '@earendil-works/pi-coding-agent';\nimport { type Component, Text, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\nimport { DelegationManager, type DelegationProgress } from '../services/delegation/manager.ts';\nimport type {\n Task,\n TaskAction,\n TaskDetails,\n TaskItemMutation,\n TaskMutationParams,\n TaskStatus,\n} from '../services/store/types.ts';\n\nexport const STATUS_GLYPH: Record<TaskStatus, string> = {\n pending: '○',\n in_progress: '◐',\n completed: '●',\n failed: '✗',\n deleted: '⊘',\n};\n\ntype TaskStatusColor = 'dim' | 'warning' | 'success' | 'error' | 'muted';\n\nexport const STATUS_COLOR: Record<TaskStatus, TaskStatusColor> = {\n pending: 'dim',\n in_progress: 'warning',\n completed: 'success',\n failed: 'error',\n deleted: 'muted',\n};\n\nexport const STATUS_LABEL: Record<TaskStatus, string> = {\n pending: 'pending',\n in_progress: 'in progress',\n completed: 'completed',\n failed: 'failed',\n deleted: 'deleted',\n};\n\nconst OVERLAY_STATUS_GLYPH: Record<TaskStatus, string> = {\n ...STATUS_GLYPH,\n completed: '✓',\n deleted: STATUS_GLYPH.failed,\n};\n\nconst OVERLAY_STATUS_COLOR: Record<TaskStatus, TaskStatusColor> = {\n ...STATUS_COLOR,\n deleted: 'error',\n};\nconst MILLISECONDS_PER_SECOND = 1000;\nconst SECONDS_PER_MINUTE = 60;\nconst MINUTES_PER_HOUR = 60;\nconst TOKENS_PER_KILO = 1000;\nconst TOKEN_DECIMAL_LIMIT = 10_000;\nconst REDUNDANT_WORKING_PREFIX = /^working:\\s*/i;\nconst INLINE_WHITESPACE = /\\s+/gu;\n\n/** Keep dynamic content inside the overlay's one-physical-line contract. */\nfunction inlineText(text: string): string {\n return text.replace(INLINE_WHITESPACE, ' ').trim();\n}\n\n/** Thinking text already communicates activity; keep only its useful content. */\nfunction progressText(currentTool: string | undefined): string | undefined {\n if (!currentTool) return undefined;\n const text = inlineText(currentTool.replace(REDUNDANT_WORKING_PREFIX, ''));\n return text || undefined;\n}\n\nexport const ACTION_GLYPH: Record<TaskAction, string> = {\n upsert: '✎',\n delete: '×',\n get: '›',\n list: '☰',\n clear: '∅',\n assign: '⇒',\n cancel: '⊗',\n};\n\n/**\n * `upsert` is two operations behind one name, so a homogeneous batch narrows to\n * the glyph the operator already reads: `+` means new work appeared, `→` means\n * work moved. A mixed batch keeps the neutral write glyph.\n */\nconst UPSERT_CREATE_GLYPH = '+';\nconst UPSERT_UPDATE_GLYPH = '→';\n\nfunction upsertGlyph(items: readonly TaskItemMutation[]): string {\n if (items.length === 0) return ACTION_GLYPH.upsert;\n if (items.every((item) => item.id === undefined)) return UPSERT_CREATE_GLYPH;\n if (items.every((item) => item.id !== undefined)) return UPSERT_UPDATE_GLYPH;\n return ACTION_GLYPH.upsert;\n}\n\nexport function overlayStatusGlyph(status: TaskStatus, theme: Theme): string {\n return theme.fg(OVERLAY_STATUS_COLOR[status], OVERLAY_STATUS_GLYPH[status]);\n}\n\nexport function formatElapsedMs(durationMs: number): string {\n const totalSeconds = Math.max(0, Math.floor(durationMs / MILLISECONDS_PER_SECOND));\n const seconds = totalSeconds % SECONDS_PER_MINUTE;\n const totalMinutes = Math.floor(totalSeconds / SECONDS_PER_MINUTE);\n if (totalMinutes < MINUTES_PER_HOUR) {\n return totalMinutes === 0 ? `${seconds}s` : `${totalMinutes}m${String(seconds).padStart(2, '0')}s`;\n }\n const minutes = totalMinutes % MINUTES_PER_HOUR;\n const hours = Math.floor(totalMinutes / MINUTES_PER_HOUR);\n return `${hours}h${String(minutes).padStart(2, '0')}m${String(seconds).padStart(2, '0')}s`;\n}\n\nexport function formatTokenCount(tokens: number): string {\n const count = Math.max(0, Math.round(tokens));\n if (count < TOKENS_PER_KILO) return String(count);\n if (count < TOKEN_DECIMAL_LIMIT) return `${(count / TOKENS_PER_KILO).toFixed(1)}k`;\n return `${Math.round(count / TOKENS_PER_KILO)}k`;\n}\n\n/**\n * One task row for the persistent overlay.\n *\n * A delegated row carries the agent name and its current tool so the operator\n * can see that background work is progressing without opening a transcript.\n */\nexport function formatOverlayTaskLine(\n task: Task,\n theme: Theme,\n showId: boolean,\n progress?: DelegationProgress,\n nowMs = Date.now(),\n agentColor?: Parameters<Theme['fg']>[0],\n): string {\n const subjectColor =\n task.status === 'in_progress'\n ? 'accent'\n : task.status === 'completed' || task.status === 'deleted'\n ? 'muted'\n : 'text';\n let subject = theme.fg(subjectColor, inlineText(task.subject));\n if (task.status === 'completed' || task.status === 'deleted') {\n subject = theme.strikethrough(subject);\n }\n\n let line = overlayStatusGlyph(task.status, theme);\n if (showId) line += ` ${theme.fg('dim', `#${task.id}`)}`;\n line += ` ${subject}`;\n\n const agent = task.delegation?.agent;\n const agentLabel = agent ? inlineText(agent) : undefined;\n if (agentLabel && task.delegation?.state === 'running') {\n // No progress means no timing is known, so the chips are omitted rather than\n // reported as `[0s]`. The transcript renderer has no progress feed at all.\n const chips: string[] = [];\n if (progress) {\n chips.push(theme.fg('muted', `[${formatElapsedMs(DelegationManager.elapsedMs(progress, nowMs))}]`));\n if (progress.tokens !== undefined) chips.push(theme.fg('muted', `[${formatTokenCount(progress.tokens)}]`));\n }\n const identity = task.delegation.runId ?? task.delegation.requestId;\n chips.push(theme.fg(agentColor ?? agentIdentityColor(identity), `[${agentLabel}]`));\n line += ` ${chips.join('')}`;\n const activity = progressText(progress?.currentTool);\n if (activity) line += ` ${theme.fg('muted', `· ${activity}`)}`;\n } else if (agentLabel) {\n line += ` ${theme.fg('muted', `[${agentLabel}]`)}`;\n }\n\n if (task.status === 'in_progress' && task.activeForm) {\n line += ` ${theme.fg('muted', `(${inlineText(task.activeForm)})`)}`;\n }\n if (task.blockedBy?.length) {\n line += ` ${theme.fg('muted', `⛓ ${task.blockedBy.map((id) => `#${id}`).join(',')}`)}`;\n }\n return line;\n}\n\n/** One task line for the `/tasks` command output. */\nexport function formatCommandTaskLine(task: Task, glyph: string): string {\n const form = task.status === 'in_progress' && task.activeForm ? ` (${task.activeForm})` : '';\n const agent = task.delegation?.agent ? ` [${task.delegation.agent}]` : '';\n const block = task.blockedBy?.length ? ` ⛓ ${task.blockedBy.map((id) => `#${id}`).join(',')}` : '';\n return ` ${glyph} #${task.id} ${task.subject}${form}${agent}${block}`;\n}\n\nexport function renderTaskCall(\n args: TaskMutationParams & { action: TaskAction },\n theme: Theme,\n tasks: readonly Task[],\n): Component {\n const items = args.action === 'upsert' ? (args.tasks ?? []) : [];\n const glyph = args.action === 'upsert' ? upsertGlyph(items) : (ACTION_GLYPH[args.action] ?? args.action);\n let text = renderToolHeading('task', `${glyph} ${args.action}`, theme);\n\n if (args.action === 'upsert') {\n // One entry reads exactly as create or update did; a batch reads as a count\n // rather than a subject it would have to pick arbitrarily.\n if (items.length === 1) {\n const only = items[0];\n if (only.id === undefined) {\n if (only.subject) text += ` ${theme.fg('dim', only.subject)}`;\n } else {\n const subject = tasks.find((task) => task.id === only.id)?.subject ?? only.subject ?? `#${only.id}`;\n text += ` ${theme.fg('accent', subject)}`;\n }\n } else if (items.length > 1) {\n text += ` ${theme.fg('dim', `${items.length} tasks`)}`;\n }\n } else if (args.action === 'assign') {\n const assignments = args.assignments ?? [];\n if (assignments.length === 1) {\n const only = assignments[0];\n const subject = tasks.find((task) => task.id === only.id)?.subject ?? `#${only.id}`;\n text += ` ${theme.fg('accent', subject)}`;\n if (only.agent) text += ` ${theme.fg('muted', `→ ${only.agent}`)}`;\n } else if (assignments.length > 1) {\n text += ` ${theme.fg('dim', `${assignments.length} tasks`)}`;\n }\n } else if ((args.action === 'get' || args.action === 'delete' || args.action === 'cancel') && args.id !== undefined) {\n const subject = tasks.find((task) => task.id === args.id)?.subject ?? `#${args.id}`;\n text += ` ${theme.fg('accent', subject)}`;\n } else if (args.action === 'list' && args.status) {\n text += ` ${theme.fg('muted', STATUS_LABEL[args.status])}`;\n }\n return new DoomToolCall(text);\n}\n\n/** Task rows kept in a collapsed result, before `ctrl+o` reveals the rest. */\nconst TRANSCRIPT_TASK_ROWS = 8;\n/** Minimum gap between a row's subject and its right-aligned status. */\nconst STATUS_GUTTER = 2;\n\nexport interface JustifiedRow {\n left: string;\n /** Pinned to the right edge. The subject is truncated before this is dropped. */\n right?: string;\n}\n\n/**\n * Rows whose trailing text is flushed right.\n *\n * `Text` cannot do this: the status has to be placed against the viewport edge,\n * which is only known at render time.\n */\nexport class JustifiedRows implements Component {\n constructor(private readonly rows: readonly JustifiedRow[]) {}\n\n /**\n * Nothing to discard: the rows are themed by the caller and held verbatim, and\n * Pi rebuilds this component from `renderResult` whenever the theme changes.\n */\n invalidate(): void {}\n\n render(width: number): string[] {\n return this.rows.map(({ left, right }) => {\n if (right === undefined || right.length === 0) return truncateToWidth(left, width);\n const rightWidth = visibleWidth(right);\n const room = width - rightWidth - STATUS_GUTTER;\n if (room <= 0) return truncateToWidth(left, width);\n const subject = truncateToWidth(left, room);\n const gap = Math.max(STATUS_GUTTER, width - visibleWidth(subject) - rightWidth);\n return subject + ' '.repeat(gap) + right;\n });\n }\n}\n\n/**\n * Self-owned task shell with the same structural divider as the bash tool.\n *\n * Task rows already carry precise per-task state, so the divider stays neutral\n * instead of competing with or appearing to animate between those states.\n */\nclass TaskResultFrame implements Component {\n constructor(\n private readonly content: Component,\n private readonly theme: Theme,\n ) {}\n\n render(width: number): string[] {\n if (width <= 0) return [];\n if (width < 3) return this.content.render(width);\n\n const contentWidth = width - 2;\n const divider = this.theme.fg('borderMuted', '─'.repeat(contentWidth));\n const lines = this.content.render(contentWidth).map((line) => ` ${line}`);\n return [` ${divider}`, ...lines, ''];\n }\n\n invalidate(): void {\n this.content.invalidate();\n }\n}\n\nexport interface TaskResultLike {\n content?: Array<{ type: string; text?: string }>;\n details?: unknown;\n isError?: boolean;\n}\n\nexport interface TaskResultOptions {\n expanded: boolean;\n isPartial: boolean;\n}\n\n/** Concatenated result text, which is all a thrown failure leaves behind. */\nfunction contentText(result: TaskResultLike): string {\n return (result.content ?? []).map((block) => block.text ?? '').join('');\n}\n\nfunction nonEmptyLines(text: string): string[] {\n return text.split('\\n').filter((line) => line.trim().length > 0);\n}\n\n/**\n * The rows `list` reported to the model.\n *\n * `details.tasks` is the whole post-mutation document, so the filters applied by\n * `formatContent` have to be applied again here or the transcript would disagree\n * with the text the model was given.\n */\nfunction listedTasks(details: TaskDetails): Task[] {\n const params = details.params as TaskMutationParams;\n let view = details.tasks;\n if (params.includeDeleted !== true) view = view.filter((task) => task.status !== 'deleted');\n if (params.status) view = view.filter((task) => task.status === params.status);\n return view;\n}\n\n/** The single task an id-bearing action acted on, so the transcript names it rather than a bare tick. */\nfunction subjectTask(details: TaskDetails): Task | undefined {\n const params = details.params as TaskMutationParams;\n if (params.id === undefined) return undefined;\n return details.tasks.find((task) => task.id === params.id);\n}\n\n/**\n * Tasks an upsert applied, resolved from the ids the reducer reported.\n *\n * The ids are read rather than guessed from the tail of the list: a batch\n * touches many rows, and a failed entry contributes none.\n */\nfunction upsertTasks(details: TaskDetails): Task[] {\n return (details.upsert?.applied ?? []).flatMap((id) => details.tasks.find((task) => task.id === id) ?? []);\n}\n\n/** Tasks successfully handed off by a native assignment batch. */\nfunction assignedTasks(details: TaskDetails): Task[] {\n return (details.assignment?.assigned ?? []).flatMap((id) => details.tasks.find((task) => task.id === id) ?? []);\n}\n\nfunction renderTaskResultBody(result: TaskResultLike, options: TaskResultOptions, theme: Theme): Component {\n const details = result.details as TaskDetails | undefined;\n\n // Running. `assign` and `cancel` stream a placeholder before the run settles.\n if (options.isPartial) {\n const text = nonEmptyLines(contentText(result)).at(-1) ?? 'working';\n return new Text(theme.fg('warning', '◐') + theme.fg('dim', ` ${text}`), 0, 0);\n }\n\n // Failed. Every failure path throws, so Pi hands back error text and no\n // details; without this the block would render a green tick on a failure.\n if (result.isError === true || details?.error !== undefined) {\n const message = details?.error ?? contentText(result);\n const all = nonEmptyLines(message);\n if (all.length <= 1) {\n return new Text(theme.fg('error', '✗') + theme.fg('dim', all[0] ? ` ${all[0]}` : ''), 0, 0);\n }\n const body = options.expanded ? all : all.slice(0, TRANSCRIPT_TASK_ROWS);\n return new Text([...body.map((line) => theme.fg('toolOutput', line)), theme.fg('error', '✗')].join('\\n'), 0, 0);\n }\n\n if (!details) return new Text(theme.fg('success', '✓'), 0, 0);\n\n /** Status rides on the task's own row, flushed right, rather than costing a line. */\n const row = (task: Task): JustifiedRow => ({\n left: formatOverlayTaskLine(task, theme, true),\n right: theme.fg(STATUS_COLOR[task.status], STATUS_LABEL[task.status]),\n });\n\n if (details.action === 'list') {\n const tasks = listedTasks(details);\n const shown = options.expanded ? tasks : tasks.slice(0, TRANSCRIPT_TASK_ROWS);\n const rows: JustifiedRow[] = shown.map(row);\n let summary = tasks.length === 0 ? 'no tasks' : `${tasks.length} task${tasks.length === 1 ? '' : 's'}`;\n if (tasks.length > shown.length) summary += ' · ctrl+o';\n rows.push({ left: theme.fg('success', '✓') + theme.fg('dim', ` ${summary}`) });\n return new JustifiedRows(rows);\n }\n\n if (details.action === 'clear') {\n return new Text(theme.fg('success', '✓') + theme.fg('dim', ' cleared'), 0, 0);\n }\n\n if (details.action === 'assign' && details.assignment) {\n const assigned = assignedTasks(details);\n const failed = details.assignment.failed;\n if (assigned.length === 1 && failed === 0) return new JustifiedRows([row(assigned[0])]);\n\n const shown = options.expanded ? assigned : assigned.slice(0, TRANSCRIPT_TASK_ROWS);\n const rows: JustifiedRow[] = shown.map(row);\n let summary = `${assigned.length} assigned`;\n if (failed > 0) summary += ` · ${failed} failed`;\n if (assigned.length > shown.length) summary += ' · ctrl+o';\n const mark = failed > 0 ? theme.fg('warning', '!') : theme.fg('success', '✓');\n rows.push({ left: mark + theme.fg('dim', ` ${summary}`) });\n return new JustifiedRows(rows);\n }\n\n if (details.action === 'upsert') {\n const touched = upsertTasks(details);\n const failed = details.upsert?.failed ?? 0;\n // One applied task and nothing failed is the common case: keep the single\n // row it has always had rather than dressing it as a bulk report.\n if (touched.length === 1 && failed === 0) return new JustifiedRows([row(touched[0])]);\n\n const shown = options.expanded ? touched : touched.slice(0, TRANSCRIPT_TASK_ROWS);\n const rows: JustifiedRow[] = shown.map(row);\n let summary = `${touched.length} applied`;\n if (failed > 0) summary += ` · ${failed} failed`;\n if (touched.length > shown.length) summary += ' · ctrl+o';\n // Failed entries get no row: there is no task to render, and their messages\n // are already in the text the model was given.\n const mark = failed > 0 ? theme.fg('warning', '!') : theme.fg('success', '✓');\n rows.push({ left: mark + theme.fg('dim', ` ${summary}`) });\n return new JustifiedRows(rows);\n }\n\n const task = subjectTask(details);\n if (!task) return new Text(theme.fg('success', '✓'), 0, 0);\n return new JustifiedRows([row(task)]);\n}\n\nexport function renderTaskResult(result: TaskResultLike, options: TaskResultOptions, theme: Theme): Component {\n return new TaskResultFrame(renderTaskResultBody(result, options, theme), theme);\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.cts","names":[],"sources":["../../src/tui/format.ts"],"mappings":";;;;;cAca,cAAc,OAAO;KAQ7B;cAEQ,cAAc,OAAO,YAAY;cAQjC,cAAc,OAAO;
|
|
1
|
+
{"version":3,"file":"format.d.cts","names":[],"sources":["../../src/tui/format.ts"],"mappings":";;;;;cAca,cAAc,OAAO;KAQ7B;cAEQ,cAAc,OAAO,YAAY;cAQjC,cAAc,OAAO;cAsCrB,cAAc,OAAO;iBAyBlB,mBAAmB,QAAQ,YAAY,OAAO;iBAI9C,gBAAgB;iBAYhB,iBAAiB;;;;;;;iBAajB,sBACd,MAAM,MACN,OAAO,OACP,iBACA,WAAW,oBACX,gBACA,aAAa,WAAW;;iBA8CV,sBAAsB,MAAM,MAAM;iBAOlC,eACd,MAAM;EAAuB,QAAQ;GACrC,OAAO,OACP,gBAAgB,SACf;UA2Cc;EACf;;EAEA;;;;;;;;cASW,yBAAyB;mBACP;EAAA,YAAA,eAAe;;;;;EAM5C;EAEA,OAAO;;UAwCQ;EACf,UAAU;IAAQ;IAAc;;EAChC;EACA;;UAGe;EACf;EACA;;iBAmIc,iBAAiB,QAAQ,gBAAgB,SAAS,mBAAmB,OAAO,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.mts","names":[],"sources":["../../src/tui/format.ts"],"mappings":";;;;;cAca,cAAc,OAAO;KAQ7B;cAEQ,cAAc,OAAO,YAAY;cAQjC,cAAc,OAAO;
|
|
1
|
+
{"version":3,"file":"format.d.mts","names":[],"sources":["../../src/tui/format.ts"],"mappings":";;;;;cAca,cAAc,OAAO;KAQ7B;cAEQ,cAAc,OAAO,YAAY;cAQjC,cAAc,OAAO;cAsCrB,cAAc,OAAO;iBAyBlB,mBAAmB,QAAQ,YAAY,OAAO;iBAI9C,gBAAgB;iBAYhB,iBAAiB;;;;;;;iBAajB,sBACd,MAAM,MACN,OAAO,OACP,iBACA,WAAW,oBACX,gBACA,aAAa,WAAW;;iBA8CV,sBAAsB,MAAM,MAAM;iBAOlC,eACd,MAAM;EAAuB,QAAQ;GACrC,OAAO,OACP,gBAAgB,SACf;UA2Cc;EACf;;EAEA;;;;;;;;cASW,yBAAyB;mBACP;EAAA,YAAA,eAAe;;;;;EAM5C;EAEA,OAAO;;UAwCQ;EACf,UAAU;IAAQ;IAAc;;EAChC;EACA;;UAGe;EACf;EACA;;iBAmIc,iBAAiB,QAAQ,gBAAgB,SAAS,mBAAmB,OAAO,QAAQ"}
|
package/dist/tui/format.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{DelegationManager as e}from"../services/delegation/manager.mjs";import{Text as t,truncateToWidth as n,visibleWidth as r}from"@earendil-works/pi-tui";import{agentIdentityColor as i}from"@agimon-ai/doompi-ui/theme";import{DoomToolCall as a,renderToolHeading as o}from"@agimon-ai/doompi-ui/toolChrome";const s={pending:`○`,in_progress:`◐`,completed:`●`,failed:`✗`,deleted:`⊘`},c={pending:`dim`,in_progress:`warning`,completed:`success`,failed:`error`,deleted:`muted`},l={pending:`pending`,in_progress:`in progress`,completed:`completed`,failed:`failed`,deleted:`deleted`},u={...s,completed:`✓`,deleted:s.failed},d={...c,deleted:`error`},f=1e3,p=/^working:\s*/i;function
|
|
2
|
-
`).filter(e=>e.trim().length>0)}function
|
|
3
|
-
`),0,0)}if(!i)return new t(r.fg(`success`,`✓`),0,0);let a=e=>({left:
|
|
1
|
+
import{DelegationManager as e}from"../services/delegation/manager.mjs";import{Text as t,truncateToWidth as n,visibleWidth as r}from"@earendil-works/pi-tui";import{agentIdentityColor as i}from"@agimon-ai/doompi-ui/theme";import{DoomToolCall as a,renderToolHeading as o}from"@agimon-ai/doompi-ui/toolChrome";const s={pending:`○`,in_progress:`◐`,completed:`●`,failed:`✗`,deleted:`⊘`},c={pending:`dim`,in_progress:`warning`,completed:`success`,failed:`error`,deleted:`muted`},l={pending:`pending`,in_progress:`in progress`,completed:`completed`,failed:`failed`,deleted:`deleted`},u={...s,completed:`✓`,deleted:s.failed},d={...c,deleted:`error`},f=1e3,p=/^working:\s*/i,m=/\s+/gu;function h(e){return e.replace(m,` `).trim()}function g(e){if(e)return h(e.replace(p,``))||void 0}const _={upsert:`✎`,delete:`×`,get:`›`,list:`☰`,clear:`∅`,assign:`⇒`,cancel:`⊗`};function v(e){return e.length===0?_.upsert:e.every(e=>e.id===void 0)?`+`:e.every(e=>e.id!==void 0)?`→`:_.upsert}function y(e,t){return t.fg(d[e],u[e])}function b(e){let t=Math.max(0,Math.floor(e/1e3)),n=t%60,r=Math.floor(t/60);if(r<60)return r===0?`${n}s`:`${r}m${String(n).padStart(2,`0`)}s`;let i=r%60;return`${Math.floor(r/60)}h${String(i).padStart(2,`0`)}m${String(n).padStart(2,`0`)}s`}function x(e){let t=Math.max(0,Math.round(e));return t<f?String(t):t<1e4?`${(t/f).toFixed(1)}k`:`${Math.round(t/f)}k`}function S(t,n,r,a,o=Date.now(),s){let c=t.status===`in_progress`?`accent`:t.status===`completed`||t.status===`deleted`?`muted`:`text`,l=n.fg(c,h(t.subject));(t.status===`completed`||t.status===`deleted`)&&(l=n.strikethrough(l));let u=y(t.status,n);r&&(u+=` ${n.fg(`dim`,`#${t.id}`)}`),u+=` ${l}`;let d=t.delegation?.agent,f=d?h(d):void 0;if(f&&t.delegation?.state===`running`){let r=[];a&&(r.push(n.fg(`muted`,`[${b(e.elapsedMs(a,o))}]`)),a.tokens!==void 0&&r.push(n.fg(`muted`,`[${x(a.tokens)}]`)));let c=t.delegation.runId??t.delegation.requestId;r.push(n.fg(s??i(c),`[${f}]`)),u+=` ${r.join(``)}`;let l=g(a?.currentTool);l&&(u+=` ${n.fg(`muted`,`· ${l}`)}`)}else f&&(u+=` ${n.fg(`muted`,`[${f}]`)}`);return t.status===`in_progress`&&t.activeForm&&(u+=` ${n.fg(`muted`,`(${h(t.activeForm)})`)}`),t.blockedBy?.length&&(u+=` ${n.fg(`muted`,`⛓ ${t.blockedBy.map(e=>`#${e}`).join(`,`)}`)}`),u}function C(e,t){let n=e.status===`in_progress`&&e.activeForm?` (${e.activeForm})`:``,r=e.delegation?.agent?` [${e.delegation.agent}]`:``,i=e.blockedBy?.length?` ⛓ ${e.blockedBy.map(e=>`#${e}`).join(`,`)}`:``;return` ${t} #${e.id} ${e.subject}${n}${r}${i}`}function w(e,t,n){let r=e.action===`upsert`?e.tasks??[]:[],i=e.action===`upsert`?v(r):_[e.action]??e.action,s=o(`task`,`${i} ${e.action}`,t);if(e.action===`upsert`){if(r.length===1){let e=r[0];if(e.id===void 0)e.subject&&(s+=` ${t.fg(`dim`,e.subject)}`);else{let r=n.find(t=>t.id===e.id)?.subject??e.subject??`#${e.id}`;s+=` ${t.fg(`accent`,r)}`}}else r.length>1&&(s+=` ${t.fg(`dim`,`${r.length} tasks`)}`)}else if(e.action===`assign`){let r=e.assignments??[];if(r.length===1){let e=r[0],i=n.find(t=>t.id===e.id)?.subject??`#${e.id}`;s+=` ${t.fg(`accent`,i)}`,e.agent&&(s+=` ${t.fg(`muted`,`→ ${e.agent}`)}`)}else r.length>1&&(s+=` ${t.fg(`dim`,`${r.length} tasks`)}`)}else if((e.action===`get`||e.action===`delete`||e.action===`cancel`)&&e.id!==void 0){let r=n.find(t=>t.id===e.id)?.subject??`#${e.id}`;s+=` ${t.fg(`accent`,r)}`}else e.action===`list`&&e.status&&(s+=` ${t.fg(`muted`,l[e.status])}`);return new a(s)}var T=class{rows;constructor(e){this.rows=e}invalidate(){}render(e){return this.rows.map(({left:t,right:i})=>{if(i===void 0||i.length===0)return n(t,e);let a=r(i),o=e-a-2;if(o<=0)return n(t,e);let s=n(t,o),c=Math.max(2,e-r(s)-a);return s+` `.repeat(c)+i})}},E=class{content;theme;constructor(e,t){this.content=e,this.theme=t}render(e){if(e<=0)return[];if(e<3)return this.content.render(e);let t=e-2,n=this.theme.fg(`borderMuted`,`─`.repeat(t)),r=this.content.render(t).map(e=>` ${e}`);return[` ${n}`,...r,``]}invalidate(){this.content.invalidate()}};function D(e){return(e.content??[]).map(e=>e.text??``).join(``)}function O(e){return e.split(`
|
|
2
|
+
`).filter(e=>e.trim().length>0)}function k(e){let t=e.params,n=e.tasks;return t.includeDeleted!==!0&&(n=n.filter(e=>e.status!==`deleted`)),t.status&&(n=n.filter(e=>e.status===t.status)),n}function A(e){let t=e.params;if(t.id!==void 0)return e.tasks.find(e=>e.id===t.id)}function j(e){return(e.upsert?.applied??[]).flatMap(t=>e.tasks.find(e=>e.id===t)??[])}function M(e){return(e.assignment?.assigned??[]).flatMap(t=>e.tasks.find(e=>e.id===t)??[])}function N(e,n,r){let i=e.details;if(n.isPartial){let n=O(D(e)).at(-1)??`working`;return new t(r.fg(`warning`,`◐`)+r.fg(`dim`,` ${n}`),0,0)}if(e.isError===!0||i?.error!==void 0){let a=O(i?.error??D(e));if(a.length<=1)return new t(r.fg(`error`,`✗`)+r.fg(`dim`,a[0]?` ${a[0]}`:``),0,0);let o=n.expanded?a:a.slice(0,8);return new t([...o.map(e=>r.fg(`toolOutput`,e)),r.fg(`error`,`✗`)].join(`
|
|
3
|
+
`),0,0)}if(!i)return new t(r.fg(`success`,`✓`),0,0);let a=e=>({left:S(e,r,!0),right:r.fg(c[e.status],l[e.status])});if(i.action===`list`){let e=k(i),t=n.expanded?e:e.slice(0,8),o=t.map(a),s=e.length===0?`no tasks`:`${e.length} task${e.length===1?``:`s`}`;return e.length>t.length&&(s+=` · ctrl+o`),o.push({left:r.fg(`success`,`✓`)+r.fg(`dim`,` ${s}`)}),new T(o)}if(i.action===`clear`)return new t(r.fg(`success`,`✓`)+r.fg(`dim`,` cleared`),0,0);if(i.action===`assign`&&i.assignment){let e=M(i),t=i.assignment.failed;if(e.length===1&&t===0)return new T([a(e[0])]);let o=n.expanded?e:e.slice(0,8),s=o.map(a),c=`${e.length} assigned`;t>0&&(c+=` · ${t} failed`),e.length>o.length&&(c+=` · ctrl+o`);let l=t>0?r.fg(`warning`,`!`):r.fg(`success`,`✓`);return s.push({left:l+r.fg(`dim`,` ${c}`)}),new T(s)}if(i.action===`upsert`){let e=j(i),t=i.upsert?.failed??0;if(e.length===1&&t===0)return new T([a(e[0])]);let o=n.expanded?e:e.slice(0,8),s=o.map(a),c=`${e.length} applied`;t>0&&(c+=` · ${t} failed`),e.length>o.length&&(c+=` · ctrl+o`);let l=t>0?r.fg(`warning`,`!`):r.fg(`success`,`✓`);return s.push({left:l+r.fg(`dim`,` ${c}`)}),new T(s)}let o=A(i);return o?new T([a(o)]):new t(r.fg(`success`,`✓`),0,0)}function P(e,t,n){return new E(N(e,t,n),n)}export{_ as ACTION_GLYPH,T as JustifiedRows,c as STATUS_COLOR,s as STATUS_GLYPH,l as STATUS_LABEL,C as formatCommandTaskLine,b as formatElapsedMs,S as formatOverlayTaskLine,x as formatTokenCount,y as overlayStatusGlyph,w as renderTaskCall,P as renderTaskResult};
|
|
4
4
|
//# sourceMappingURL=format.mjs.map
|
package/dist/tui/format.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.mjs","names":[],"sources":["../../src/tui/format.ts"],"sourcesContent":["import { agentIdentityColor } from '@agimon-ai/doompi-ui/theme';\nimport { DoomToolCall, renderToolHeading } from '@agimon-ai/doompi-ui/toolChrome';\nimport type { Theme } from '@earendil-works/pi-coding-agent';\nimport { type Component, Text, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\nimport { DelegationManager, type DelegationProgress } from '../services/delegation/manager.ts';\nimport type {\n Task,\n TaskAction,\n TaskDetails,\n TaskItemMutation,\n TaskMutationParams,\n TaskStatus,\n} from '../services/store/types.ts';\n\nexport const STATUS_GLYPH: Record<TaskStatus, string> = {\n pending: '○',\n in_progress: '◐',\n completed: '●',\n failed: '✗',\n deleted: '⊘',\n};\n\ntype TaskStatusColor = 'dim' | 'warning' | 'success' | 'error' | 'muted';\n\nexport const STATUS_COLOR: Record<TaskStatus, TaskStatusColor> = {\n pending: 'dim',\n in_progress: 'warning',\n completed: 'success',\n failed: 'error',\n deleted: 'muted',\n};\n\nexport const STATUS_LABEL: Record<TaskStatus, string> = {\n pending: 'pending',\n in_progress: 'in progress',\n completed: 'completed',\n failed: 'failed',\n deleted: 'deleted',\n};\n\nconst OVERLAY_STATUS_GLYPH: Record<TaskStatus, string> = {\n ...STATUS_GLYPH,\n completed: '✓',\n deleted: STATUS_GLYPH.failed,\n};\n\nconst OVERLAY_STATUS_COLOR: Record<TaskStatus, TaskStatusColor> = {\n ...STATUS_COLOR,\n deleted: 'error',\n};\nconst MILLISECONDS_PER_SECOND = 1000;\nconst SECONDS_PER_MINUTE = 60;\nconst MINUTES_PER_HOUR = 60;\nconst TOKENS_PER_KILO = 1000;\nconst TOKEN_DECIMAL_LIMIT = 10_000;\nconst REDUNDANT_WORKING_PREFIX = /^working:\\s*/i;\n\n/** Thinking text already communicates activity; keep only its useful content. */\nfunction progressText(currentTool: string | undefined): string | undefined {\n const text = currentTool?.replace(REDUNDANT_WORKING_PREFIX, '').trim();\n return text || undefined;\n}\n\nexport const ACTION_GLYPH: Record<TaskAction, string> = {\n upsert: '✎',\n delete: '×',\n get: '›',\n list: '☰',\n clear: '∅',\n assign: '⇒',\n cancel: '⊗',\n};\n\n/**\n * `upsert` is two operations behind one name, so a homogeneous batch narrows to\n * the glyph the operator already reads: `+` means new work appeared, `→` means\n * work moved. A mixed batch keeps the neutral write glyph.\n */\nconst UPSERT_CREATE_GLYPH = '+';\nconst UPSERT_UPDATE_GLYPH = '→';\n\nfunction upsertGlyph(items: readonly TaskItemMutation[]): string {\n if (items.length === 0) return ACTION_GLYPH.upsert;\n if (items.every((item) => item.id === undefined)) return UPSERT_CREATE_GLYPH;\n if (items.every((item) => item.id !== undefined)) return UPSERT_UPDATE_GLYPH;\n return ACTION_GLYPH.upsert;\n}\n\nexport function overlayStatusGlyph(status: TaskStatus, theme: Theme): string {\n return theme.fg(OVERLAY_STATUS_COLOR[status], OVERLAY_STATUS_GLYPH[status]);\n}\n\nexport function formatElapsedMs(durationMs: number): string {\n const totalSeconds = Math.max(0, Math.floor(durationMs / MILLISECONDS_PER_SECOND));\n const seconds = totalSeconds % SECONDS_PER_MINUTE;\n const totalMinutes = Math.floor(totalSeconds / SECONDS_PER_MINUTE);\n if (totalMinutes < MINUTES_PER_HOUR) {\n return totalMinutes === 0 ? `${seconds}s` : `${totalMinutes}m${String(seconds).padStart(2, '0')}s`;\n }\n const minutes = totalMinutes % MINUTES_PER_HOUR;\n const hours = Math.floor(totalMinutes / MINUTES_PER_HOUR);\n return `${hours}h${String(minutes).padStart(2, '0')}m${String(seconds).padStart(2, '0')}s`;\n}\n\nexport function formatTokenCount(tokens: number): string {\n const count = Math.max(0, Math.round(tokens));\n if (count < TOKENS_PER_KILO) return String(count);\n if (count < TOKEN_DECIMAL_LIMIT) return `${(count / TOKENS_PER_KILO).toFixed(1)}k`;\n return `${Math.round(count / TOKENS_PER_KILO)}k`;\n}\n\n/**\n * One task row for the persistent overlay.\n *\n * A delegated row carries the agent name and its current tool so the operator\n * can see that background work is progressing without opening a transcript.\n */\nexport function formatOverlayTaskLine(\n task: Task,\n theme: Theme,\n showId: boolean,\n progress?: DelegationProgress,\n nowMs = Date.now(),\n agentColor?: Parameters<Theme['fg']>[0],\n): string {\n const subjectColor =\n task.status === 'in_progress'\n ? 'accent'\n : task.status === 'completed' || task.status === 'deleted'\n ? 'muted'\n : 'text';\n let subject = theme.fg(subjectColor, task.subject);\n if (task.status === 'completed' || task.status === 'deleted') {\n subject = theme.strikethrough(subject);\n }\n\n let line = overlayStatusGlyph(task.status, theme);\n if (showId) line += ` ${theme.fg('dim', `#${task.id}`)}`;\n line += ` ${subject}`;\n\n const agent = task.delegation?.agent;\n if (agent && task.delegation?.state === 'running') {\n // No progress means no timing is known, so the chips are omitted rather than\n // reported as `[0s]`. The transcript renderer has no progress feed at all.\n const chips: string[] = [];\n if (progress) {\n chips.push(theme.fg('muted', `[${formatElapsedMs(DelegationManager.elapsedMs(progress, nowMs))}]`));\n if (progress.tokens !== undefined) chips.push(theme.fg('muted', `[${formatTokenCount(progress.tokens)}]`));\n }\n const identity = task.delegation.runId ?? task.delegation.requestId;\n chips.push(theme.fg(agentColor ?? agentIdentityColor(identity), `[${agent}]`));\n line += ` ${chips.join('')}`;\n const activity = progressText(progress?.currentTool);\n if (activity) line += ` ${theme.fg('muted', `· ${activity}`)}`;\n } else if (agent) {\n line += ` ${theme.fg('muted', `[${agent}]`)}`;\n }\n\n if (task.status === 'in_progress' && task.activeForm) {\n line += ` ${theme.fg('muted', `(${task.activeForm})`)}`;\n }\n if (task.blockedBy?.length) {\n line += ` ${theme.fg('muted', `⛓ ${task.blockedBy.map((id) => `#${id}`).join(',')}`)}`;\n }\n return line;\n}\n\n/** One task line for the `/tasks` command output. */\nexport function formatCommandTaskLine(task: Task, glyph: string): string {\n const form = task.status === 'in_progress' && task.activeForm ? ` (${task.activeForm})` : '';\n const agent = task.delegation?.agent ? ` [${task.delegation.agent}]` : '';\n const block = task.blockedBy?.length ? ` ⛓ ${task.blockedBy.map((id) => `#${id}`).join(',')}` : '';\n return ` ${glyph} #${task.id} ${task.subject}${form}${agent}${block}`;\n}\n\nexport function renderTaskCall(\n args: TaskMutationParams & { action: TaskAction },\n theme: Theme,\n tasks: readonly Task[],\n): Component {\n const items = args.action === 'upsert' ? (args.tasks ?? []) : [];\n const glyph = args.action === 'upsert' ? upsertGlyph(items) : (ACTION_GLYPH[args.action] ?? args.action);\n let text = renderToolHeading('task', `${glyph} ${args.action}`, theme);\n\n if (args.action === 'upsert') {\n // One entry reads exactly as create or update did; a batch reads as a count\n // rather than a subject it would have to pick arbitrarily.\n if (items.length === 1) {\n const only = items[0];\n if (only.id === undefined) {\n if (only.subject) text += ` ${theme.fg('dim', only.subject)}`;\n } else {\n const subject = tasks.find((task) => task.id === only.id)?.subject ?? only.subject ?? `#${only.id}`;\n text += ` ${theme.fg('accent', subject)}`;\n }\n } else if (items.length > 1) {\n text += ` ${theme.fg('dim', `${items.length} tasks`)}`;\n }\n } else if (args.action === 'assign') {\n const assignments = args.assignments ?? [];\n if (assignments.length === 1) {\n const only = assignments[0];\n const subject = tasks.find((task) => task.id === only.id)?.subject ?? `#${only.id}`;\n text += ` ${theme.fg('accent', subject)}`;\n if (only.agent) text += ` ${theme.fg('muted', `→ ${only.agent}`)}`;\n } else if (assignments.length > 1) {\n text += ` ${theme.fg('dim', `${assignments.length} tasks`)}`;\n }\n } else if ((args.action === 'get' || args.action === 'delete' || args.action === 'cancel') && args.id !== undefined) {\n const subject = tasks.find((task) => task.id === args.id)?.subject ?? `#${args.id}`;\n text += ` ${theme.fg('accent', subject)}`;\n } else if (args.action === 'list' && args.status) {\n text += ` ${theme.fg('muted', STATUS_LABEL[args.status])}`;\n }\n return new DoomToolCall(text);\n}\n\n/** Task rows kept in a collapsed result, before `ctrl+o` reveals the rest. */\nconst TRANSCRIPT_TASK_ROWS = 8;\n/** Minimum gap between a row's subject and its right-aligned status. */\nconst STATUS_GUTTER = 2;\n\nexport interface JustifiedRow {\n left: string;\n /** Pinned to the right edge. The subject is truncated before this is dropped. */\n right?: string;\n}\n\n/**\n * Rows whose trailing text is flushed right.\n *\n * `Text` cannot do this: the status has to be placed against the viewport edge,\n * which is only known at render time.\n */\nexport class JustifiedRows implements Component {\n constructor(private readonly rows: readonly JustifiedRow[]) {}\n\n /**\n * Nothing to discard: the rows are themed by the caller and held verbatim, and\n * Pi rebuilds this component from `renderResult` whenever the theme changes.\n */\n invalidate(): void {}\n\n render(width: number): string[] {\n return this.rows.map(({ left, right }) => {\n if (right === undefined || right.length === 0) return truncateToWidth(left, width);\n const rightWidth = visibleWidth(right);\n const room = width - rightWidth - STATUS_GUTTER;\n if (room <= 0) return truncateToWidth(left, width);\n const subject = truncateToWidth(left, room);\n const gap = Math.max(STATUS_GUTTER, width - visibleWidth(subject) - rightWidth);\n return subject + ' '.repeat(gap) + right;\n });\n }\n}\n\n/**\n * Self-owned task shell with the same structural divider as the bash tool.\n *\n * Task rows already carry precise per-task state, so the divider stays neutral\n * instead of competing with or appearing to animate between those states.\n */\nclass TaskResultFrame implements Component {\n constructor(\n private readonly content: Component,\n private readonly theme: Theme,\n ) {}\n\n render(width: number): string[] {\n if (width <= 0) return [];\n if (width < 3) return this.content.render(width);\n\n const contentWidth = width - 2;\n const divider = this.theme.fg('borderMuted', '─'.repeat(contentWidth));\n const lines = this.content.render(contentWidth).map((line) => ` ${line}`);\n return [` ${divider}`, ...lines, ''];\n }\n\n invalidate(): void {\n this.content.invalidate();\n }\n}\n\nexport interface TaskResultLike {\n content?: Array<{ type: string; text?: string }>;\n details?: unknown;\n isError?: boolean;\n}\n\nexport interface TaskResultOptions {\n expanded: boolean;\n isPartial: boolean;\n}\n\n/** Concatenated result text, which is all a thrown failure leaves behind. */\nfunction contentText(result: TaskResultLike): string {\n return (result.content ?? []).map((block) => block.text ?? '').join('');\n}\n\nfunction nonEmptyLines(text: string): string[] {\n return text.split('\\n').filter((line) => line.trim().length > 0);\n}\n\n/**\n * The rows `list` reported to the model.\n *\n * `details.tasks` is the whole post-mutation document, so the filters applied by\n * `formatContent` have to be applied again here or the transcript would disagree\n * with the text the model was given.\n */\nfunction listedTasks(details: TaskDetails): Task[] {\n const params = details.params as TaskMutationParams;\n let view = details.tasks;\n if (params.includeDeleted !== true) view = view.filter((task) => task.status !== 'deleted');\n if (params.status) view = view.filter((task) => task.status === params.status);\n return view;\n}\n\n/** The single task an id-bearing action acted on, so the transcript names it rather than a bare tick. */\nfunction subjectTask(details: TaskDetails): Task | undefined {\n const params = details.params as TaskMutationParams;\n if (params.id === undefined) return undefined;\n return details.tasks.find((task) => task.id === params.id);\n}\n\n/**\n * Tasks an upsert applied, resolved from the ids the reducer reported.\n *\n * The ids are read rather than guessed from the tail of the list: a batch\n * touches many rows, and a failed entry contributes none.\n */\nfunction upsertTasks(details: TaskDetails): Task[] {\n return (details.upsert?.applied ?? []).flatMap((id) => details.tasks.find((task) => task.id === id) ?? []);\n}\n\n/** Tasks successfully handed off by a native assignment batch. */\nfunction assignedTasks(details: TaskDetails): Task[] {\n return (details.assignment?.assigned ?? []).flatMap((id) => details.tasks.find((task) => task.id === id) ?? []);\n}\n\nfunction renderTaskResultBody(result: TaskResultLike, options: TaskResultOptions, theme: Theme): Component {\n const details = result.details as TaskDetails | undefined;\n\n // Running. `assign` and `cancel` stream a placeholder before the run settles.\n if (options.isPartial) {\n const text = nonEmptyLines(contentText(result)).at(-1) ?? 'working';\n return new Text(theme.fg('warning', '◐') + theme.fg('dim', ` ${text}`), 0, 0);\n }\n\n // Failed. Every failure path throws, so Pi hands back error text and no\n // details; without this the block would render a green tick on a failure.\n if (result.isError === true || details?.error !== undefined) {\n const message = details?.error ?? contentText(result);\n const all = nonEmptyLines(message);\n if (all.length <= 1) {\n return new Text(theme.fg('error', '✗') + theme.fg('dim', all[0] ? ` ${all[0]}` : ''), 0, 0);\n }\n const body = options.expanded ? all : all.slice(0, TRANSCRIPT_TASK_ROWS);\n return new Text([...body.map((line) => theme.fg('toolOutput', line)), theme.fg('error', '✗')].join('\\n'), 0, 0);\n }\n\n if (!details) return new Text(theme.fg('success', '✓'), 0, 0);\n\n /** Status rides on the task's own row, flushed right, rather than costing a line. */\n const row = (task: Task): JustifiedRow => ({\n left: formatOverlayTaskLine(task, theme, true),\n right: theme.fg(STATUS_COLOR[task.status], STATUS_LABEL[task.status]),\n });\n\n if (details.action === 'list') {\n const tasks = listedTasks(details);\n const shown = options.expanded ? tasks : tasks.slice(0, TRANSCRIPT_TASK_ROWS);\n const rows: JustifiedRow[] = shown.map(row);\n let summary = tasks.length === 0 ? 'no tasks' : `${tasks.length} task${tasks.length === 1 ? '' : 's'}`;\n if (tasks.length > shown.length) summary += ' · ctrl+o';\n rows.push({ left: theme.fg('success', '✓') + theme.fg('dim', ` ${summary}`) });\n return new JustifiedRows(rows);\n }\n\n if (details.action === 'clear') {\n return new Text(theme.fg('success', '✓') + theme.fg('dim', ' cleared'), 0, 0);\n }\n\n if (details.action === 'assign' && details.assignment) {\n const assigned = assignedTasks(details);\n const failed = details.assignment.failed;\n if (assigned.length === 1 && failed === 0) return new JustifiedRows([row(assigned[0])]);\n\n const shown = options.expanded ? assigned : assigned.slice(0, TRANSCRIPT_TASK_ROWS);\n const rows: JustifiedRow[] = shown.map(row);\n let summary = `${assigned.length} assigned`;\n if (failed > 0) summary += ` · ${failed} failed`;\n if (assigned.length > shown.length) summary += ' · ctrl+o';\n const mark = failed > 0 ? theme.fg('warning', '!') : theme.fg('success', '✓');\n rows.push({ left: mark + theme.fg('dim', ` ${summary}`) });\n return new JustifiedRows(rows);\n }\n\n if (details.action === 'upsert') {\n const touched = upsertTasks(details);\n const failed = details.upsert?.failed ?? 0;\n // One applied task and nothing failed is the common case: keep the single\n // row it has always had rather than dressing it as a bulk report.\n if (touched.length === 1 && failed === 0) return new JustifiedRows([row(touched[0])]);\n\n const shown = options.expanded ? touched : touched.slice(0, TRANSCRIPT_TASK_ROWS);\n const rows: JustifiedRow[] = shown.map(row);\n let summary = `${touched.length} applied`;\n if (failed > 0) summary += ` · ${failed} failed`;\n if (touched.length > shown.length) summary += ' · ctrl+o';\n // Failed entries get no row: there is no task to render, and their messages\n // are already in the text the model was given.\n const mark = failed > 0 ? theme.fg('warning', '!') : theme.fg('success', '✓');\n rows.push({ left: mark + theme.fg('dim', ` ${summary}`) });\n return new JustifiedRows(rows);\n }\n\n const task = subjectTask(details);\n if (!task) return new Text(theme.fg('success', '✓'), 0, 0);\n return new JustifiedRows([row(task)]);\n}\n\nexport function renderTaskResult(result: TaskResultLike, options: TaskResultOptions, theme: Theme): Component {\n return new TaskResultFrame(renderTaskResultBody(result, options, theme), theme);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"format.mjs","names":[],"sources":["../../src/tui/format.ts"],"sourcesContent":["import { agentIdentityColor } from '@agimon-ai/doompi-ui/theme';\nimport { DoomToolCall, renderToolHeading } from '@agimon-ai/doompi-ui/toolChrome';\nimport type { Theme } from '@earendil-works/pi-coding-agent';\nimport { type Component, Text, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\nimport { DelegationManager, type DelegationProgress } from '../services/delegation/manager.ts';\nimport type {\n Task,\n TaskAction,\n TaskDetails,\n TaskItemMutation,\n TaskMutationParams,\n TaskStatus,\n} from '../services/store/types.ts';\n\nexport const STATUS_GLYPH: Record<TaskStatus, string> = {\n pending: '○',\n in_progress: '◐',\n completed: '●',\n failed: '✗',\n deleted: '⊘',\n};\n\ntype TaskStatusColor = 'dim' | 'warning' | 'success' | 'error' | 'muted';\n\nexport const STATUS_COLOR: Record<TaskStatus, TaskStatusColor> = {\n pending: 'dim',\n in_progress: 'warning',\n completed: 'success',\n failed: 'error',\n deleted: 'muted',\n};\n\nexport const STATUS_LABEL: Record<TaskStatus, string> = {\n pending: 'pending',\n in_progress: 'in progress',\n completed: 'completed',\n failed: 'failed',\n deleted: 'deleted',\n};\n\nconst OVERLAY_STATUS_GLYPH: Record<TaskStatus, string> = {\n ...STATUS_GLYPH,\n completed: '✓',\n deleted: STATUS_GLYPH.failed,\n};\n\nconst OVERLAY_STATUS_COLOR: Record<TaskStatus, TaskStatusColor> = {\n ...STATUS_COLOR,\n deleted: 'error',\n};\nconst MILLISECONDS_PER_SECOND = 1000;\nconst SECONDS_PER_MINUTE = 60;\nconst MINUTES_PER_HOUR = 60;\nconst TOKENS_PER_KILO = 1000;\nconst TOKEN_DECIMAL_LIMIT = 10_000;\nconst REDUNDANT_WORKING_PREFIX = /^working:\\s*/i;\nconst INLINE_WHITESPACE = /\\s+/gu;\n\n/** Keep dynamic content inside the overlay's one-physical-line contract. */\nfunction inlineText(text: string): string {\n return text.replace(INLINE_WHITESPACE, ' ').trim();\n}\n\n/** Thinking text already communicates activity; keep only its useful content. */\nfunction progressText(currentTool: string | undefined): string | undefined {\n if (!currentTool) return undefined;\n const text = inlineText(currentTool.replace(REDUNDANT_WORKING_PREFIX, ''));\n return text || undefined;\n}\n\nexport const ACTION_GLYPH: Record<TaskAction, string> = {\n upsert: '✎',\n delete: '×',\n get: '›',\n list: '☰',\n clear: '∅',\n assign: '⇒',\n cancel: '⊗',\n};\n\n/**\n * `upsert` is two operations behind one name, so a homogeneous batch narrows to\n * the glyph the operator already reads: `+` means new work appeared, `→` means\n * work moved. A mixed batch keeps the neutral write glyph.\n */\nconst UPSERT_CREATE_GLYPH = '+';\nconst UPSERT_UPDATE_GLYPH = '→';\n\nfunction upsertGlyph(items: readonly TaskItemMutation[]): string {\n if (items.length === 0) return ACTION_GLYPH.upsert;\n if (items.every((item) => item.id === undefined)) return UPSERT_CREATE_GLYPH;\n if (items.every((item) => item.id !== undefined)) return UPSERT_UPDATE_GLYPH;\n return ACTION_GLYPH.upsert;\n}\n\nexport function overlayStatusGlyph(status: TaskStatus, theme: Theme): string {\n return theme.fg(OVERLAY_STATUS_COLOR[status], OVERLAY_STATUS_GLYPH[status]);\n}\n\nexport function formatElapsedMs(durationMs: number): string {\n const totalSeconds = Math.max(0, Math.floor(durationMs / MILLISECONDS_PER_SECOND));\n const seconds = totalSeconds % SECONDS_PER_MINUTE;\n const totalMinutes = Math.floor(totalSeconds / SECONDS_PER_MINUTE);\n if (totalMinutes < MINUTES_PER_HOUR) {\n return totalMinutes === 0 ? `${seconds}s` : `${totalMinutes}m${String(seconds).padStart(2, '0')}s`;\n }\n const minutes = totalMinutes % MINUTES_PER_HOUR;\n const hours = Math.floor(totalMinutes / MINUTES_PER_HOUR);\n return `${hours}h${String(minutes).padStart(2, '0')}m${String(seconds).padStart(2, '0')}s`;\n}\n\nexport function formatTokenCount(tokens: number): string {\n const count = Math.max(0, Math.round(tokens));\n if (count < TOKENS_PER_KILO) return String(count);\n if (count < TOKEN_DECIMAL_LIMIT) return `${(count / TOKENS_PER_KILO).toFixed(1)}k`;\n return `${Math.round(count / TOKENS_PER_KILO)}k`;\n}\n\n/**\n * One task row for the persistent overlay.\n *\n * A delegated row carries the agent name and its current tool so the operator\n * can see that background work is progressing without opening a transcript.\n */\nexport function formatOverlayTaskLine(\n task: Task,\n theme: Theme,\n showId: boolean,\n progress?: DelegationProgress,\n nowMs = Date.now(),\n agentColor?: Parameters<Theme['fg']>[0],\n): string {\n const subjectColor =\n task.status === 'in_progress'\n ? 'accent'\n : task.status === 'completed' || task.status === 'deleted'\n ? 'muted'\n : 'text';\n let subject = theme.fg(subjectColor, inlineText(task.subject));\n if (task.status === 'completed' || task.status === 'deleted') {\n subject = theme.strikethrough(subject);\n }\n\n let line = overlayStatusGlyph(task.status, theme);\n if (showId) line += ` ${theme.fg('dim', `#${task.id}`)}`;\n line += ` ${subject}`;\n\n const agent = task.delegation?.agent;\n const agentLabel = agent ? inlineText(agent) : undefined;\n if (agentLabel && task.delegation?.state === 'running') {\n // No progress means no timing is known, so the chips are omitted rather than\n // reported as `[0s]`. The transcript renderer has no progress feed at all.\n const chips: string[] = [];\n if (progress) {\n chips.push(theme.fg('muted', `[${formatElapsedMs(DelegationManager.elapsedMs(progress, nowMs))}]`));\n if (progress.tokens !== undefined) chips.push(theme.fg('muted', `[${formatTokenCount(progress.tokens)}]`));\n }\n const identity = task.delegation.runId ?? task.delegation.requestId;\n chips.push(theme.fg(agentColor ?? agentIdentityColor(identity), `[${agentLabel}]`));\n line += ` ${chips.join('')}`;\n const activity = progressText(progress?.currentTool);\n if (activity) line += ` ${theme.fg('muted', `· ${activity}`)}`;\n } else if (agentLabel) {\n line += ` ${theme.fg('muted', `[${agentLabel}]`)}`;\n }\n\n if (task.status === 'in_progress' && task.activeForm) {\n line += ` ${theme.fg('muted', `(${inlineText(task.activeForm)})`)}`;\n }\n if (task.blockedBy?.length) {\n line += ` ${theme.fg('muted', `⛓ ${task.blockedBy.map((id) => `#${id}`).join(',')}`)}`;\n }\n return line;\n}\n\n/** One task line for the `/tasks` command output. */\nexport function formatCommandTaskLine(task: Task, glyph: string): string {\n const form = task.status === 'in_progress' && task.activeForm ? ` (${task.activeForm})` : '';\n const agent = task.delegation?.agent ? ` [${task.delegation.agent}]` : '';\n const block = task.blockedBy?.length ? ` ⛓ ${task.blockedBy.map((id) => `#${id}`).join(',')}` : '';\n return ` ${glyph} #${task.id} ${task.subject}${form}${agent}${block}`;\n}\n\nexport function renderTaskCall(\n args: TaskMutationParams & { action: TaskAction },\n theme: Theme,\n tasks: readonly Task[],\n): Component {\n const items = args.action === 'upsert' ? (args.tasks ?? []) : [];\n const glyph = args.action === 'upsert' ? upsertGlyph(items) : (ACTION_GLYPH[args.action] ?? args.action);\n let text = renderToolHeading('task', `${glyph} ${args.action}`, theme);\n\n if (args.action === 'upsert') {\n // One entry reads exactly as create or update did; a batch reads as a count\n // rather than a subject it would have to pick arbitrarily.\n if (items.length === 1) {\n const only = items[0];\n if (only.id === undefined) {\n if (only.subject) text += ` ${theme.fg('dim', only.subject)}`;\n } else {\n const subject = tasks.find((task) => task.id === only.id)?.subject ?? only.subject ?? `#${only.id}`;\n text += ` ${theme.fg('accent', subject)}`;\n }\n } else if (items.length > 1) {\n text += ` ${theme.fg('dim', `${items.length} tasks`)}`;\n }\n } else if (args.action === 'assign') {\n const assignments = args.assignments ?? [];\n if (assignments.length === 1) {\n const only = assignments[0];\n const subject = tasks.find((task) => task.id === only.id)?.subject ?? `#${only.id}`;\n text += ` ${theme.fg('accent', subject)}`;\n if (only.agent) text += ` ${theme.fg('muted', `→ ${only.agent}`)}`;\n } else if (assignments.length > 1) {\n text += ` ${theme.fg('dim', `${assignments.length} tasks`)}`;\n }\n } else if ((args.action === 'get' || args.action === 'delete' || args.action === 'cancel') && args.id !== undefined) {\n const subject = tasks.find((task) => task.id === args.id)?.subject ?? `#${args.id}`;\n text += ` ${theme.fg('accent', subject)}`;\n } else if (args.action === 'list' && args.status) {\n text += ` ${theme.fg('muted', STATUS_LABEL[args.status])}`;\n }\n return new DoomToolCall(text);\n}\n\n/** Task rows kept in a collapsed result, before `ctrl+o` reveals the rest. */\nconst TRANSCRIPT_TASK_ROWS = 8;\n/** Minimum gap between a row's subject and its right-aligned status. */\nconst STATUS_GUTTER = 2;\n\nexport interface JustifiedRow {\n left: string;\n /** Pinned to the right edge. The subject is truncated before this is dropped. */\n right?: string;\n}\n\n/**\n * Rows whose trailing text is flushed right.\n *\n * `Text` cannot do this: the status has to be placed against the viewport edge,\n * which is only known at render time.\n */\nexport class JustifiedRows implements Component {\n constructor(private readonly rows: readonly JustifiedRow[]) {}\n\n /**\n * Nothing to discard: the rows are themed by the caller and held verbatim, and\n * Pi rebuilds this component from `renderResult` whenever the theme changes.\n */\n invalidate(): void {}\n\n render(width: number): string[] {\n return this.rows.map(({ left, right }) => {\n if (right === undefined || right.length === 0) return truncateToWidth(left, width);\n const rightWidth = visibleWidth(right);\n const room = width - rightWidth - STATUS_GUTTER;\n if (room <= 0) return truncateToWidth(left, width);\n const subject = truncateToWidth(left, room);\n const gap = Math.max(STATUS_GUTTER, width - visibleWidth(subject) - rightWidth);\n return subject + ' '.repeat(gap) + right;\n });\n }\n}\n\n/**\n * Self-owned task shell with the same structural divider as the bash tool.\n *\n * Task rows already carry precise per-task state, so the divider stays neutral\n * instead of competing with or appearing to animate between those states.\n */\nclass TaskResultFrame implements Component {\n constructor(\n private readonly content: Component,\n private readonly theme: Theme,\n ) {}\n\n render(width: number): string[] {\n if (width <= 0) return [];\n if (width < 3) return this.content.render(width);\n\n const contentWidth = width - 2;\n const divider = this.theme.fg('borderMuted', '─'.repeat(contentWidth));\n const lines = this.content.render(contentWidth).map((line) => ` ${line}`);\n return [` ${divider}`, ...lines, ''];\n }\n\n invalidate(): void {\n this.content.invalidate();\n }\n}\n\nexport interface TaskResultLike {\n content?: Array<{ type: string; text?: string }>;\n details?: unknown;\n isError?: boolean;\n}\n\nexport interface TaskResultOptions {\n expanded: boolean;\n isPartial: boolean;\n}\n\n/** Concatenated result text, which is all a thrown failure leaves behind. */\nfunction contentText(result: TaskResultLike): string {\n return (result.content ?? []).map((block) => block.text ?? '').join('');\n}\n\nfunction nonEmptyLines(text: string): string[] {\n return text.split('\\n').filter((line) => line.trim().length > 0);\n}\n\n/**\n * The rows `list` reported to the model.\n *\n * `details.tasks` is the whole post-mutation document, so the filters applied by\n * `formatContent` have to be applied again here or the transcript would disagree\n * with the text the model was given.\n */\nfunction listedTasks(details: TaskDetails): Task[] {\n const params = details.params as TaskMutationParams;\n let view = details.tasks;\n if (params.includeDeleted !== true) view = view.filter((task) => task.status !== 'deleted');\n if (params.status) view = view.filter((task) => task.status === params.status);\n return view;\n}\n\n/** The single task an id-bearing action acted on, so the transcript names it rather than a bare tick. */\nfunction subjectTask(details: TaskDetails): Task | undefined {\n const params = details.params as TaskMutationParams;\n if (params.id === undefined) return undefined;\n return details.tasks.find((task) => task.id === params.id);\n}\n\n/**\n * Tasks an upsert applied, resolved from the ids the reducer reported.\n *\n * The ids are read rather than guessed from the tail of the list: a batch\n * touches many rows, and a failed entry contributes none.\n */\nfunction upsertTasks(details: TaskDetails): Task[] {\n return (details.upsert?.applied ?? []).flatMap((id) => details.tasks.find((task) => task.id === id) ?? []);\n}\n\n/** Tasks successfully handed off by a native assignment batch. */\nfunction assignedTasks(details: TaskDetails): Task[] {\n return (details.assignment?.assigned ?? []).flatMap((id) => details.tasks.find((task) => task.id === id) ?? []);\n}\n\nfunction renderTaskResultBody(result: TaskResultLike, options: TaskResultOptions, theme: Theme): Component {\n const details = result.details as TaskDetails | undefined;\n\n // Running. `assign` and `cancel` stream a placeholder before the run settles.\n if (options.isPartial) {\n const text = nonEmptyLines(contentText(result)).at(-1) ?? 'working';\n return new Text(theme.fg('warning', '◐') + theme.fg('dim', ` ${text}`), 0, 0);\n }\n\n // Failed. Every failure path throws, so Pi hands back error text and no\n // details; without this the block would render a green tick on a failure.\n if (result.isError === true || details?.error !== undefined) {\n const message = details?.error ?? contentText(result);\n const all = nonEmptyLines(message);\n if (all.length <= 1) {\n return new Text(theme.fg('error', '✗') + theme.fg('dim', all[0] ? ` ${all[0]}` : ''), 0, 0);\n }\n const body = options.expanded ? all : all.slice(0, TRANSCRIPT_TASK_ROWS);\n return new Text([...body.map((line) => theme.fg('toolOutput', line)), theme.fg('error', '✗')].join('\\n'), 0, 0);\n }\n\n if (!details) return new Text(theme.fg('success', '✓'), 0, 0);\n\n /** Status rides on the task's own row, flushed right, rather than costing a line. */\n const row = (task: Task): JustifiedRow => ({\n left: formatOverlayTaskLine(task, theme, true),\n right: theme.fg(STATUS_COLOR[task.status], STATUS_LABEL[task.status]),\n });\n\n if (details.action === 'list') {\n const tasks = listedTasks(details);\n const shown = options.expanded ? tasks : tasks.slice(0, TRANSCRIPT_TASK_ROWS);\n const rows: JustifiedRow[] = shown.map(row);\n let summary = tasks.length === 0 ? 'no tasks' : `${tasks.length} task${tasks.length === 1 ? '' : 's'}`;\n if (tasks.length > shown.length) summary += ' · ctrl+o';\n rows.push({ left: theme.fg('success', '✓') + theme.fg('dim', ` ${summary}`) });\n return new JustifiedRows(rows);\n }\n\n if (details.action === 'clear') {\n return new Text(theme.fg('success', '✓') + theme.fg('dim', ' cleared'), 0, 0);\n }\n\n if (details.action === 'assign' && details.assignment) {\n const assigned = assignedTasks(details);\n const failed = details.assignment.failed;\n if (assigned.length === 1 && failed === 0) return new JustifiedRows([row(assigned[0])]);\n\n const shown = options.expanded ? assigned : assigned.slice(0, TRANSCRIPT_TASK_ROWS);\n const rows: JustifiedRow[] = shown.map(row);\n let summary = `${assigned.length} assigned`;\n if (failed > 0) summary += ` · ${failed} failed`;\n if (assigned.length > shown.length) summary += ' · ctrl+o';\n const mark = failed > 0 ? theme.fg('warning', '!') : theme.fg('success', '✓');\n rows.push({ left: mark + theme.fg('dim', ` ${summary}`) });\n return new JustifiedRows(rows);\n }\n\n if (details.action === 'upsert') {\n const touched = upsertTasks(details);\n const failed = details.upsert?.failed ?? 0;\n // One applied task and nothing failed is the common case: keep the single\n // row it has always had rather than dressing it as a bulk report.\n if (touched.length === 1 && failed === 0) return new JustifiedRows([row(touched[0])]);\n\n const shown = options.expanded ? touched : touched.slice(0, TRANSCRIPT_TASK_ROWS);\n const rows: JustifiedRow[] = shown.map(row);\n let summary = `${touched.length} applied`;\n if (failed > 0) summary += ` · ${failed} failed`;\n if (touched.length > shown.length) summary += ' · ctrl+o';\n // Failed entries get no row: there is no task to render, and their messages\n // are already in the text the model was given.\n const mark = failed > 0 ? theme.fg('warning', '!') : theme.fg('success', '✓');\n rows.push({ left: mark + theme.fg('dim', ` ${summary}`) });\n return new JustifiedRows(rows);\n }\n\n const task = subjectTask(details);\n if (!task) return new Text(theme.fg('success', '✓'), 0, 0);\n return new JustifiedRows([row(task)]);\n}\n\nexport function renderTaskResult(result: TaskResultLike, options: TaskResultOptions, theme: Theme): Component {\n return new TaskResultFrame(renderTaskResultBody(result, options, theme), theme);\n}\n"],"mappings":"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"}
|
package/dist/tui/selectors.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){return e.filter(e=>e.status!==`deleted`)}function t(e){let t=new Map;for(let n of e)t.has(n.id)||t.set(n.id,n.status);return t}function n(e,t){return e.blockedBy?.some(e=>{let n=t.get(e);return n!==void 0&&n!==`completed`&&n!==`deleted`})??!1}function r(e){let r=t(e),i=[],a={total:0,completed:0,inProgress:0,pending:0,failed:0,blocked:0},o={inProgress:[],blocked:[],pending:[],completed:[],failed:[]},s=!1;for(let t of e)if(t.status!==`deleted`)switch(i.push(t),a.total+=1,s||=!!t.blockedBy?.length,t.status){case`in_progress`:a.inProgress+=1,o.inProgress.push(t);break;case`pending`:a.pending+=1,n(t,r)?(a.blocked+=1,o.blocked.push(t)):o.pending.push(t);break;case`failed`:a.failed+=1,o.failed.push(t);break;case`completed`:a.completed+=1,o.completed.push(t)}return{visible:i,counts:a,groups:o,showIds:s,hasActiveWork:a.inProgress>0}}function i(e){return r(e).counts}function a(e){return r(e).groups}function o(e){return e.some(e=>e.status!==`deleted`&&!!e.blockedBy?.length)}function s(e){return e.some(e=>e.status===`in_progress`)}function c(e,t){let
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){return e.filter(e=>e.status!==`deleted`)}function t(e){let t=new Map;for(let n of e)t.has(n.id)||t.set(n.id,n.status);return t}function n(e,t){return e.blockedBy?.some(e=>{let n=t.get(e);return n!==void 0&&n!==`completed`&&n!==`deleted`})??!1}function r(e){let r=t(e),i=[],a={total:0,completed:0,inProgress:0,pending:0,failed:0,blocked:0},o={inProgress:[],blocked:[],pending:[],completed:[],failed:[]},s=!1;for(let t of e)if(t.status!==`deleted`)switch(i.push(t),a.total+=1,s||=!!t.blockedBy?.length,t.status){case`in_progress`:a.inProgress+=1,o.inProgress.push(t);break;case`pending`:a.pending+=1,n(t,r)?(a.blocked+=1,o.blocked.push(t)):o.pending.push(t);break;case`failed`:a.failed+=1,o.failed.push(t);break;case`completed`:a.completed+=1,o.completed.push(t)}return{visible:i,counts:a,groups:o,showIds:s,hasActiveWork:a.inProgress>0}}function i(e){return r(e).counts}function a(e){return r(e).groups}function o(e){return e.some(e=>e.status!==`deleted`&&!!e.blockedBy?.length)}function s(e){return e.some(e=>e.status===`in_progress`)}function c(e,t){let{groups:n,visible:r}=e,i=[...n.inProgress,...n.failed,...n.blocked,...n.pending];if(t<=0)return{visible:[],hiddenCompleted:n.completed.length,truncatedTail:i.length};if(r.length<=t)return{visible:r,hiddenCompleted:0,truncatedTail:0};if(i.length<=t){let e=t-i.length,a=n.completed.slice(0,e),o=new Set([...i,...a]);return{visible:r.filter(e=>o.has(e)),hiddenCompleted:n.completed.length-a.length,truncatedTail:0}}let a=new Set(i.slice(0,t));return{visible:r.filter(e=>a.has(e)),hiddenCompleted:n.completed.length,truncatedTail:i.length-t}}function l(e,t){return c(r(e),t)}exports.countTasks=i,exports.deriveTaskProjection=r,exports.groupTasks=a,exports.hasActiveWork=s,exports.selectOverlayLayout=l,exports.selectOverlayLayoutFromProjection=c,exports.shouldShowIds=o,exports.visibleTasks=e;
|
|
2
2
|
//# sourceMappingURL=selectors.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selectors.cjs","names":[],"sources":["../../src/tui/selectors.ts"],"sourcesContent":["import type { Task } from '../services/store/types.ts';\n\nexport interface TaskCounts {\n total: number;\n completed: number;\n inProgress: number;\n pending: number;\n failed: number;\n blocked: number;\n}\n\nexport interface TaskGroups {\n inProgress: Task[];\n blocked: Task[];\n pending: Task[];\n completed: Task[];\n failed: Task[];\n}\n\nexport interface TaskProjection {\n visible: Task[];\n counts: TaskCounts;\n groups: TaskGroups;\n showIds: boolean;\n hasActiveWork: boolean;\n}\n\nexport function visibleTasks(tasks: readonly Task[]): Task[] {\n return tasks.filter((task) => task.status !== 'deleted');\n}\n\nfunction indexStatuses(tasks: readonly Task[]): Map<number, Task['status']> {\n const statuses = new Map<number, Task['status']>();\n for (const task of tasks) {\n // Valid stores have unique ids. Keeping the first entry also preserves the\n // former `Array.find` behavior if a corrupt document contains duplicates.\n if (!statuses.has(task.id)) statuses.set(task.id, task.status);\n }\n return statuses;\n}\n\nfunction hasUnresolvedBlocker(task: Task, statuses: ReadonlyMap<number, Task['status']>): boolean {\n return (\n task.blockedBy?.some((id) => {\n const status = statuses.get(id);\n return status !== undefined && status !== 'completed' && status !== 'deleted';\n }) ?? false\n );\n}\n\n/**\n * Derive all overlay-facing state in O(tasks + dependency edges).\n *\n * The id index replaces one full task-list scan per dependency, while the\n * ordered pass keeps group ordering identical to the source document.\n */\nexport function deriveTaskProjection(tasks: readonly Task[]): TaskProjection {\n const statuses = indexStatuses(tasks);\n const visible: Task[] = [];\n const counts: TaskCounts = { total: 0, completed: 0, inProgress: 0, pending: 0, failed: 0, blocked: 0 };\n const groups: TaskGroups = { inProgress: [], blocked: [], pending: [], completed: [], failed: [] };\n let showIds = false;\n\n for (const task of tasks) {\n if (task.status === 'deleted') continue;\n\n visible.push(task);\n counts.total += 1;\n showIds ||= Boolean(task.blockedBy?.length);\n\n switch (task.status) {\n case 'in_progress':\n counts.inProgress += 1;\n groups.inProgress.push(task);\n break;\n case 'pending': {\n counts.pending += 1;\n if (hasUnresolvedBlocker(task, statuses)) {\n counts.blocked += 1;\n groups.blocked.push(task);\n } else {\n groups.pending.push(task);\n }\n break;\n }\n case 'failed':\n counts.failed += 1;\n groups.failed.push(task);\n break;\n case 'completed':\n counts.completed += 1;\n groups.completed.push(task);\n break;\n }\n }\n\n return { visible, counts, groups, showIds, hasActiveWork: counts.inProgress > 0 };\n}\n\nexport function countTasks(tasks: readonly Task[]): TaskCounts {\n return deriveTaskProjection(tasks).counts;\n}\n\n/**\n * Group tasks for display.\n *\n * Blocked tasks split out of `pending` because a reader scanning the overlay\n * needs to know at a glance what is actually actionable right now.\n */\nexport function groupTasks(tasks: readonly Task[]): TaskGroups {\n return deriveTaskProjection(tasks).groups;\n}\n\n/** Ids are only worth the visual noise once dependencies are in play. */\nexport function shouldShowIds(tasks: readonly Task[]): boolean {\n return tasks.some((task) => task.status !== 'deleted' && Boolean(task.blockedBy?.length));\n}\n\nexport function hasActiveWork(tasks: readonly Task[]): boolean {\n return tasks.some((task) => task.status === 'in_progress');\n}\n\nexport interface OverlayLayout {\n visible: Task[];\n hiddenCompleted: number;\n truncatedTail: number;\n}\n\n/**\n * Choose which rows fit the overlay budget.\n *\n * Active work wins
|
|
1
|
+
{"version":3,"file":"selectors.cjs","names":[],"sources":["../../src/tui/selectors.ts"],"sourcesContent":["import type { Task } from '../services/store/types.ts';\n\nexport interface TaskCounts {\n total: number;\n completed: number;\n inProgress: number;\n pending: number;\n failed: number;\n blocked: number;\n}\n\nexport interface TaskGroups {\n inProgress: Task[];\n blocked: Task[];\n pending: Task[];\n completed: Task[];\n failed: Task[];\n}\n\nexport interface TaskProjection {\n visible: Task[];\n counts: TaskCounts;\n groups: TaskGroups;\n showIds: boolean;\n hasActiveWork: boolean;\n}\n\nexport function visibleTasks(tasks: readonly Task[]): Task[] {\n return tasks.filter((task) => task.status !== 'deleted');\n}\n\nfunction indexStatuses(tasks: readonly Task[]): Map<number, Task['status']> {\n const statuses = new Map<number, Task['status']>();\n for (const task of tasks) {\n // Valid stores have unique ids. Keeping the first entry also preserves the\n // former `Array.find` behavior if a corrupt document contains duplicates.\n if (!statuses.has(task.id)) statuses.set(task.id, task.status);\n }\n return statuses;\n}\n\nfunction hasUnresolvedBlocker(task: Task, statuses: ReadonlyMap<number, Task['status']>): boolean {\n return (\n task.blockedBy?.some((id) => {\n const status = statuses.get(id);\n return status !== undefined && status !== 'completed' && status !== 'deleted';\n }) ?? false\n );\n}\n\n/**\n * Derive all overlay-facing state in O(tasks + dependency edges).\n *\n * The id index replaces one full task-list scan per dependency, while the\n * ordered pass keeps group ordering identical to the source document.\n */\nexport function deriveTaskProjection(tasks: readonly Task[]): TaskProjection {\n const statuses = indexStatuses(tasks);\n const visible: Task[] = [];\n const counts: TaskCounts = { total: 0, completed: 0, inProgress: 0, pending: 0, failed: 0, blocked: 0 };\n const groups: TaskGroups = { inProgress: [], blocked: [], pending: [], completed: [], failed: [] };\n let showIds = false;\n\n for (const task of tasks) {\n if (task.status === 'deleted') continue;\n\n visible.push(task);\n counts.total += 1;\n showIds ||= Boolean(task.blockedBy?.length);\n\n switch (task.status) {\n case 'in_progress':\n counts.inProgress += 1;\n groups.inProgress.push(task);\n break;\n case 'pending': {\n counts.pending += 1;\n if (hasUnresolvedBlocker(task, statuses)) {\n counts.blocked += 1;\n groups.blocked.push(task);\n } else {\n groups.pending.push(task);\n }\n break;\n }\n case 'failed':\n counts.failed += 1;\n groups.failed.push(task);\n break;\n case 'completed':\n counts.completed += 1;\n groups.completed.push(task);\n break;\n }\n }\n\n return { visible, counts, groups, showIds, hasActiveWork: counts.inProgress > 0 };\n}\n\nexport function countTasks(tasks: readonly Task[]): TaskCounts {\n return deriveTaskProjection(tasks).counts;\n}\n\n/**\n * Group tasks for display.\n *\n * Blocked tasks split out of `pending` because a reader scanning the overlay\n * needs to know at a glance what is actually actionable right now.\n */\nexport function groupTasks(tasks: readonly Task[]): TaskGroups {\n return deriveTaskProjection(tasks).groups;\n}\n\n/** Ids are only worth the visual noise once dependencies are in play. */\nexport function shouldShowIds(tasks: readonly Task[]): boolean {\n return tasks.some((task) => task.status !== 'deleted' && Boolean(task.blockedBy?.length));\n}\n\nexport function hasActiveWork(tasks: readonly Task[]): boolean {\n return tasks.some((task) => task.status === 'in_progress');\n}\n\nexport interface OverlayLayout {\n visible: Task[];\n hiddenCompleted: number;\n truncatedTail: number;\n}\n\n/**\n * Choose which rows fit the overlay budget.\n *\n * Active work wins limited space: in-progress first, then failed, blocked, and\n * pending. Selected rows are always returned in source order, however, so a\n * status update does not reshuffle an otherwise unchanged overlay.\n */\nexport function selectOverlayLayoutFromProjection(projection: TaskProjection, budget: number): OverlayLayout {\n const { groups, visible: sourceOrder } = projection;\n const active = [...groups.inProgress, ...groups.failed, ...groups.blocked, ...groups.pending];\n if (budget <= 0) {\n return {\n visible: [],\n hiddenCompleted: groups.completed.length,\n truncatedTail: active.length,\n };\n }\n if (sourceOrder.length <= budget) {\n return { visible: sourceOrder, hiddenCompleted: 0, truncatedTail: 0 };\n }\n\n if (active.length <= budget) {\n const remaining = budget - active.length;\n const shownCompleted = groups.completed.slice(0, remaining);\n const selected = new Set([...active, ...shownCompleted]);\n return {\n visible: sourceOrder.filter((task) => selected.has(task)),\n hiddenCompleted: groups.completed.length - shownCompleted.length,\n truncatedTail: 0,\n };\n }\n\n const selected = new Set(active.slice(0, budget));\n return {\n visible: sourceOrder.filter((task) => selected.has(task)),\n hiddenCompleted: groups.completed.length,\n truncatedTail: active.length - budget,\n };\n}\n\nexport function selectOverlayLayout(tasks: readonly Task[], budget: number): OverlayLayout {\n return selectOverlayLayoutFromProjection(deriveTaskProjection(tasks), budget);\n}\n"],"mappings":"mEA2BA,SAAgB,EAAa,EAAgC,CAC3D,OAAO,EAAM,OAAQ,GAAS,EAAK,SAAW,SAAS,CACzD,CAEA,SAAS,EAAc,EAAqD,CAC1E,IAAM,EAAW,IAAI,IACrB,IAAK,IAAM,KAAQ,EAGZ,EAAS,IAAI,EAAK,EAAE,GAAG,EAAS,IAAI,EAAK,GAAI,EAAK,MAAM,EAE/D,OAAO,CACT,CAEA,SAAS,EAAqB,EAAY,EAAwD,CAChG,OACE,EAAK,WAAW,KAAM,GAAO,CAC3B,IAAM,EAAS,EAAS,IAAI,CAAE,EAC9B,OAAO,IAAW,IAAA,IAAa,IAAW,aAAe,IAAW,SACtE,CAAC,GAAK,EAEV,CAQA,SAAgB,EAAqB,EAAwC,CAC3E,IAAM,EAAW,EAAc,CAAK,EAC9B,EAAkB,CAAC,EACnB,EAAqB,CAAE,MAAO,EAAG,UAAW,EAAG,WAAY,EAAG,QAAS,EAAG,OAAQ,EAAG,QAAS,CAAE,EAChG,EAAqB,CAAE,WAAY,CAAC,EAAG,QAAS,CAAC,EAAG,QAAS,CAAC,EAAG,UAAW,CAAC,EAAG,OAAQ,CAAC,CAAE,EAC7F,EAAU,GAEd,IAAK,IAAM,KAAQ,EACb,KAAK,SAAW,UAMpB,OAJA,EAAQ,KAAK,CAAI,EACjB,EAAO,OAAS,EAChB,IAAY,EAAQ,EAAK,WAAW,OAE5B,EAAK,OAAb,CACE,IAAK,cACH,EAAO,YAAc,EACrB,EAAO,WAAW,KAAK,CAAI,EAC3B,MACF,IAAK,UACH,EAAO,SAAW,EACd,EAAqB,EAAM,CAAQ,GACrC,EAAO,SAAW,EAClB,EAAO,QAAQ,KAAK,CAAI,GAExB,EAAO,QAAQ,KAAK,CAAI,EAE1B,MAEF,IAAK,SACH,EAAO,QAAU,EACjB,EAAO,OAAO,KAAK,CAAI,EACvB,MACF,IAAK,YACH,EAAO,WAAa,EACpB,EAAO,UAAU,KAAK,CAAI,CAE9B,CAGF,MAAO,CAAE,UAAS,SAAQ,SAAQ,UAAS,cAAe,EAAO,WAAa,CAAE,CAClF,CAEA,SAAgB,EAAW,EAAoC,CAC7D,OAAO,EAAqB,CAAK,CAAC,CAAC,MACrC,CAQA,SAAgB,EAAW,EAAoC,CAC7D,OAAO,EAAqB,CAAK,CAAC,CAAC,MACrC,CAGA,SAAgB,EAAc,EAAiC,CAC7D,OAAO,EAAM,KAAM,GAAS,EAAK,SAAW,WAAa,EAAQ,EAAK,WAAW,MAAO,CAC1F,CAEA,SAAgB,EAAc,EAAiC,CAC7D,OAAO,EAAM,KAAM,GAAS,EAAK,SAAW,aAAa,CAC3D,CAeA,SAAgB,EAAkC,EAA4B,EAA+B,CAC3G,GAAM,CAAE,SAAQ,QAAS,GAAgB,EACnC,EAAS,CAAC,GAAG,EAAO,WAAY,GAAG,EAAO,OAAQ,GAAG,EAAO,QAAS,GAAG,EAAO,OAAO,EAC5F,GAAI,GAAU,EACZ,MAAO,CACL,QAAS,CAAC,EACV,gBAAiB,EAAO,UAAU,OAClC,cAAe,EAAO,MACxB,EAEF,GAAI,EAAY,QAAU,EACxB,MAAO,CAAE,QAAS,EAAa,gBAAiB,EAAG,cAAe,CAAE,EAGtE,GAAI,EAAO,QAAU,EAAQ,CAC3B,IAAM,EAAY,EAAS,EAAO,OAC5B,EAAiB,EAAO,UAAU,MAAM,EAAG,CAAS,EACpD,EAAW,IAAI,IAAI,CAAC,GAAG,EAAQ,GAAG,CAAc,CAAC,EACvD,MAAO,CACL,QAAS,EAAY,OAAQ,GAAS,EAAS,IAAI,CAAI,CAAC,EACxD,gBAAiB,EAAO,UAAU,OAAS,EAAe,OAC1D,cAAe,CACjB,CACF,CAEA,IAAM,EAAW,IAAI,IAAI,EAAO,MAAM,EAAG,CAAM,CAAC,EAChD,MAAO,CACL,QAAS,EAAY,OAAQ,GAAS,EAAS,IAAI,CAAI,CAAC,EACxD,gBAAiB,EAAO,UAAU,OAClC,cAAe,EAAO,OAAS,CACjC,CACF,CAEA,SAAgB,EAAoB,EAAwB,EAA+B,CACzF,OAAO,EAAkC,EAAqB,CAAK,EAAG,CAAM,CAC9E"}
|
package/dist/tui/selectors.d.cts
CHANGED
|
@@ -46,6 +46,13 @@ interface OverlayLayout {
|
|
|
46
46
|
hiddenCompleted: number;
|
|
47
47
|
truncatedTail: number;
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Choose which rows fit the overlay budget.
|
|
51
|
+
*
|
|
52
|
+
* Active work wins limited space: in-progress first, then failed, blocked, and
|
|
53
|
+
* pending. Selected rows are always returned in source order, however, so a
|
|
54
|
+
* status update does not reshuffle an otherwise unchanged overlay.
|
|
55
|
+
*/
|
|
49
56
|
declare function selectOverlayLayoutFromProjection(projection: TaskProjection, budget: number): OverlayLayout;
|
|
50
57
|
declare function selectOverlayLayout(tasks: readonly Task[], budget: number): OverlayLayout;
|
|
51
58
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selectors.d.cts","names":[],"sources":["../../src/tui/selectors.ts"],"mappings":";;UAEiB;EACf;EACA;EACA;EACA;EACA;EACA;;UAGe;EACf,YAAY;EACZ,SAAS;EACT,SAAS;EACT,WAAW;EACX,QAAQ;;UAGO;EACf,SAAS;EACT,QAAQ;EACR,QAAQ;EACR;EACA;;iBAGc,aAAa,gBAAgB,SAAS;;;;;;;iBA6BtC,qBAAqB,gBAAgB,SAAS;iBA2C9C,WAAW,gBAAgB,SAAS;;;;;;;iBAUpC,WAAW,gBAAgB,SAAS;;iBAKpC,cAAc,gBAAgB;iBAI9B,cAAc,gBAAgB;UAI7B;EACf,SAAS;EACT;EACA
|
|
1
|
+
{"version":3,"file":"selectors.d.cts","names":[],"sources":["../../src/tui/selectors.ts"],"mappings":";;UAEiB;EACf;EACA;EACA;EACA;EACA;EACA;;UAGe;EACf,YAAY;EACZ,SAAS;EACT,SAAS;EACT,WAAW;EACX,QAAQ;;UAGO;EACf,SAAS;EACT,QAAQ;EACR,QAAQ;EACR;EACA;;iBAGc,aAAa,gBAAgB,SAAS;;;;;;;iBA6BtC,qBAAqB,gBAAgB,SAAS;iBA2C9C,WAAW,gBAAgB,SAAS;;;;;;;iBAUpC,WAAW,gBAAgB,SAAS;;iBAKpC,cAAc,gBAAgB;iBAI9B,cAAc,gBAAgB;UAI7B;EACf,SAAS;EACT;EACA;;;;;;;;;iBAUc,kCAAkC,YAAY,gBAAgB,iBAAiB;iBAiC/E,oBAAoB,gBAAgB,QAAQ,iBAAiB"}
|
package/dist/tui/selectors.d.mts
CHANGED
|
@@ -46,6 +46,13 @@ interface OverlayLayout {
|
|
|
46
46
|
hiddenCompleted: number;
|
|
47
47
|
truncatedTail: number;
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Choose which rows fit the overlay budget.
|
|
51
|
+
*
|
|
52
|
+
* Active work wins limited space: in-progress first, then failed, blocked, and
|
|
53
|
+
* pending. Selected rows are always returned in source order, however, so a
|
|
54
|
+
* status update does not reshuffle an otherwise unchanged overlay.
|
|
55
|
+
*/
|
|
49
56
|
declare function selectOverlayLayoutFromProjection(projection: TaskProjection, budget: number): OverlayLayout;
|
|
50
57
|
declare function selectOverlayLayout(tasks: readonly Task[], budget: number): OverlayLayout;
|
|
51
58
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selectors.d.mts","names":[],"sources":["../../src/tui/selectors.ts"],"mappings":";;UAEiB;EACf;EACA;EACA;EACA;EACA;EACA;;UAGe;EACf,YAAY;EACZ,SAAS;EACT,SAAS;EACT,WAAW;EACX,QAAQ;;UAGO;EACf,SAAS;EACT,QAAQ;EACR,QAAQ;EACR;EACA;;iBAGc,aAAa,gBAAgB,SAAS;;;;;;;iBA6BtC,qBAAqB,gBAAgB,SAAS;iBA2C9C,WAAW,gBAAgB,SAAS;;;;;;;iBAUpC,WAAW,gBAAgB,SAAS;;iBAKpC,cAAc,gBAAgB;iBAI9B,cAAc,gBAAgB;UAI7B;EACf,SAAS;EACT;EACA
|
|
1
|
+
{"version":3,"file":"selectors.d.mts","names":[],"sources":["../../src/tui/selectors.ts"],"mappings":";;UAEiB;EACf;EACA;EACA;EACA;EACA;EACA;;UAGe;EACf,YAAY;EACZ,SAAS;EACT,SAAS;EACT,WAAW;EACX,QAAQ;;UAGO;EACf,SAAS;EACT,QAAQ;EACR,QAAQ;EACR;EACA;;iBAGc,aAAa,gBAAgB,SAAS;;;;;;;iBA6BtC,qBAAqB,gBAAgB,SAAS;iBA2C9C,WAAW,gBAAgB,SAAS;;;;;;;iBAUpC,WAAW,gBAAgB,SAAS;;iBAKpC,cAAc,gBAAgB;iBAI9B,cAAc,gBAAgB;UAI7B;EACf,SAAS;EACT;EACA;;;;;;;;;iBAUc,kCAAkC,YAAY,gBAAgB,iBAAiB;iBAiC/E,oBAAoB,gBAAgB,QAAQ,iBAAiB"}
|
package/dist/tui/selectors.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e){return e.filter(e=>e.status!==`deleted`)}function t(e){let t=new Map;for(let n of e)t.has(n.id)||t.set(n.id,n.status);return t}function n(e,t){return e.blockedBy?.some(e=>{let n=t.get(e);return n!==void 0&&n!==`completed`&&n!==`deleted`})??!1}function r(e){let r=t(e),i=[],a={total:0,completed:0,inProgress:0,pending:0,failed:0,blocked:0},o={inProgress:[],blocked:[],pending:[],completed:[],failed:[]},s=!1;for(let t of e)if(t.status!==`deleted`)switch(i.push(t),a.total+=1,s||=!!t.blockedBy?.length,t.status){case`in_progress`:a.inProgress+=1,o.inProgress.push(t);break;case`pending`:a.pending+=1,n(t,r)?(a.blocked+=1,o.blocked.push(t)):o.pending.push(t);break;case`failed`:a.failed+=1,o.failed.push(t);break;case`completed`:a.completed+=1,o.completed.push(t)}return{visible:i,counts:a,groups:o,showIds:s,hasActiveWork:a.inProgress>0}}function i(e){return r(e).counts}function a(e){return r(e).groups}function o(e){return e.some(e=>e.status!==`deleted`&&!!e.blockedBy?.length)}function s(e){return e.some(e=>e.status===`in_progress`)}function c(e,t){let
|
|
1
|
+
function e(e){return e.filter(e=>e.status!==`deleted`)}function t(e){let t=new Map;for(let n of e)t.has(n.id)||t.set(n.id,n.status);return t}function n(e,t){return e.blockedBy?.some(e=>{let n=t.get(e);return n!==void 0&&n!==`completed`&&n!==`deleted`})??!1}function r(e){let r=t(e),i=[],a={total:0,completed:0,inProgress:0,pending:0,failed:0,blocked:0},o={inProgress:[],blocked:[],pending:[],completed:[],failed:[]},s=!1;for(let t of e)if(t.status!==`deleted`)switch(i.push(t),a.total+=1,s||=!!t.blockedBy?.length,t.status){case`in_progress`:a.inProgress+=1,o.inProgress.push(t);break;case`pending`:a.pending+=1,n(t,r)?(a.blocked+=1,o.blocked.push(t)):o.pending.push(t);break;case`failed`:a.failed+=1,o.failed.push(t);break;case`completed`:a.completed+=1,o.completed.push(t)}return{visible:i,counts:a,groups:o,showIds:s,hasActiveWork:a.inProgress>0}}function i(e){return r(e).counts}function a(e){return r(e).groups}function o(e){return e.some(e=>e.status!==`deleted`&&!!e.blockedBy?.length)}function s(e){return e.some(e=>e.status===`in_progress`)}function c(e,t){let{groups:n,visible:r}=e,i=[...n.inProgress,...n.failed,...n.blocked,...n.pending];if(t<=0)return{visible:[],hiddenCompleted:n.completed.length,truncatedTail:i.length};if(r.length<=t)return{visible:r,hiddenCompleted:0,truncatedTail:0};if(i.length<=t){let e=t-i.length,a=n.completed.slice(0,e),o=new Set([...i,...a]);return{visible:r.filter(e=>o.has(e)),hiddenCompleted:n.completed.length-a.length,truncatedTail:0}}let a=new Set(i.slice(0,t));return{visible:r.filter(e=>a.has(e)),hiddenCompleted:n.completed.length,truncatedTail:i.length-t}}function l(e,t){return c(r(e),t)}export{i as countTasks,r as deriveTaskProjection,a as groupTasks,s as hasActiveWork,l as selectOverlayLayout,c as selectOverlayLayoutFromProjection,o as shouldShowIds,e as visibleTasks};
|
|
2
2
|
//# sourceMappingURL=selectors.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selectors.mjs","names":[],"sources":["../../src/tui/selectors.ts"],"sourcesContent":["import type { Task } from '../services/store/types.ts';\n\nexport interface TaskCounts {\n total: number;\n completed: number;\n inProgress: number;\n pending: number;\n failed: number;\n blocked: number;\n}\n\nexport interface TaskGroups {\n inProgress: Task[];\n blocked: Task[];\n pending: Task[];\n completed: Task[];\n failed: Task[];\n}\n\nexport interface TaskProjection {\n visible: Task[];\n counts: TaskCounts;\n groups: TaskGroups;\n showIds: boolean;\n hasActiveWork: boolean;\n}\n\nexport function visibleTasks(tasks: readonly Task[]): Task[] {\n return tasks.filter((task) => task.status !== 'deleted');\n}\n\nfunction indexStatuses(tasks: readonly Task[]): Map<number, Task['status']> {\n const statuses = new Map<number, Task['status']>();\n for (const task of tasks) {\n // Valid stores have unique ids. Keeping the first entry also preserves the\n // former `Array.find` behavior if a corrupt document contains duplicates.\n if (!statuses.has(task.id)) statuses.set(task.id, task.status);\n }\n return statuses;\n}\n\nfunction hasUnresolvedBlocker(task: Task, statuses: ReadonlyMap<number, Task['status']>): boolean {\n return (\n task.blockedBy?.some((id) => {\n const status = statuses.get(id);\n return status !== undefined && status !== 'completed' && status !== 'deleted';\n }) ?? false\n );\n}\n\n/**\n * Derive all overlay-facing state in O(tasks + dependency edges).\n *\n * The id index replaces one full task-list scan per dependency, while the\n * ordered pass keeps group ordering identical to the source document.\n */\nexport function deriveTaskProjection(tasks: readonly Task[]): TaskProjection {\n const statuses = indexStatuses(tasks);\n const visible: Task[] = [];\n const counts: TaskCounts = { total: 0, completed: 0, inProgress: 0, pending: 0, failed: 0, blocked: 0 };\n const groups: TaskGroups = { inProgress: [], blocked: [], pending: [], completed: [], failed: [] };\n let showIds = false;\n\n for (const task of tasks) {\n if (task.status === 'deleted') continue;\n\n visible.push(task);\n counts.total += 1;\n showIds ||= Boolean(task.blockedBy?.length);\n\n switch (task.status) {\n case 'in_progress':\n counts.inProgress += 1;\n groups.inProgress.push(task);\n break;\n case 'pending': {\n counts.pending += 1;\n if (hasUnresolvedBlocker(task, statuses)) {\n counts.blocked += 1;\n groups.blocked.push(task);\n } else {\n groups.pending.push(task);\n }\n break;\n }\n case 'failed':\n counts.failed += 1;\n groups.failed.push(task);\n break;\n case 'completed':\n counts.completed += 1;\n groups.completed.push(task);\n break;\n }\n }\n\n return { visible, counts, groups, showIds, hasActiveWork: counts.inProgress > 0 };\n}\n\nexport function countTasks(tasks: readonly Task[]): TaskCounts {\n return deriveTaskProjection(tasks).counts;\n}\n\n/**\n * Group tasks for display.\n *\n * Blocked tasks split out of `pending` because a reader scanning the overlay\n * needs to know at a glance what is actually actionable right now.\n */\nexport function groupTasks(tasks: readonly Task[]): TaskGroups {\n return deriveTaskProjection(tasks).groups;\n}\n\n/** Ids are only worth the visual noise once dependencies are in play. */\nexport function shouldShowIds(tasks: readonly Task[]): boolean {\n return tasks.some((task) => task.status !== 'deleted' && Boolean(task.blockedBy?.length));\n}\n\nexport function hasActiveWork(tasks: readonly Task[]): boolean {\n return tasks.some((task) => task.status === 'in_progress');\n}\n\nexport interface OverlayLayout {\n visible: Task[];\n hiddenCompleted: number;\n truncatedTail: number;\n}\n\n/**\n * Choose which rows fit the overlay budget.\n *\n * Active work wins
|
|
1
|
+
{"version":3,"file":"selectors.mjs","names":[],"sources":["../../src/tui/selectors.ts"],"sourcesContent":["import type { Task } from '../services/store/types.ts';\n\nexport interface TaskCounts {\n total: number;\n completed: number;\n inProgress: number;\n pending: number;\n failed: number;\n blocked: number;\n}\n\nexport interface TaskGroups {\n inProgress: Task[];\n blocked: Task[];\n pending: Task[];\n completed: Task[];\n failed: Task[];\n}\n\nexport interface TaskProjection {\n visible: Task[];\n counts: TaskCounts;\n groups: TaskGroups;\n showIds: boolean;\n hasActiveWork: boolean;\n}\n\nexport function visibleTasks(tasks: readonly Task[]): Task[] {\n return tasks.filter((task) => task.status !== 'deleted');\n}\n\nfunction indexStatuses(tasks: readonly Task[]): Map<number, Task['status']> {\n const statuses = new Map<number, Task['status']>();\n for (const task of tasks) {\n // Valid stores have unique ids. Keeping the first entry also preserves the\n // former `Array.find` behavior if a corrupt document contains duplicates.\n if (!statuses.has(task.id)) statuses.set(task.id, task.status);\n }\n return statuses;\n}\n\nfunction hasUnresolvedBlocker(task: Task, statuses: ReadonlyMap<number, Task['status']>): boolean {\n return (\n task.blockedBy?.some((id) => {\n const status = statuses.get(id);\n return status !== undefined && status !== 'completed' && status !== 'deleted';\n }) ?? false\n );\n}\n\n/**\n * Derive all overlay-facing state in O(tasks + dependency edges).\n *\n * The id index replaces one full task-list scan per dependency, while the\n * ordered pass keeps group ordering identical to the source document.\n */\nexport function deriveTaskProjection(tasks: readonly Task[]): TaskProjection {\n const statuses = indexStatuses(tasks);\n const visible: Task[] = [];\n const counts: TaskCounts = { total: 0, completed: 0, inProgress: 0, pending: 0, failed: 0, blocked: 0 };\n const groups: TaskGroups = { inProgress: [], blocked: [], pending: [], completed: [], failed: [] };\n let showIds = false;\n\n for (const task of tasks) {\n if (task.status === 'deleted') continue;\n\n visible.push(task);\n counts.total += 1;\n showIds ||= Boolean(task.blockedBy?.length);\n\n switch (task.status) {\n case 'in_progress':\n counts.inProgress += 1;\n groups.inProgress.push(task);\n break;\n case 'pending': {\n counts.pending += 1;\n if (hasUnresolvedBlocker(task, statuses)) {\n counts.blocked += 1;\n groups.blocked.push(task);\n } else {\n groups.pending.push(task);\n }\n break;\n }\n case 'failed':\n counts.failed += 1;\n groups.failed.push(task);\n break;\n case 'completed':\n counts.completed += 1;\n groups.completed.push(task);\n break;\n }\n }\n\n return { visible, counts, groups, showIds, hasActiveWork: counts.inProgress > 0 };\n}\n\nexport function countTasks(tasks: readonly Task[]): TaskCounts {\n return deriveTaskProjection(tasks).counts;\n}\n\n/**\n * Group tasks for display.\n *\n * Blocked tasks split out of `pending` because a reader scanning the overlay\n * needs to know at a glance what is actually actionable right now.\n */\nexport function groupTasks(tasks: readonly Task[]): TaskGroups {\n return deriveTaskProjection(tasks).groups;\n}\n\n/** Ids are only worth the visual noise once dependencies are in play. */\nexport function shouldShowIds(tasks: readonly Task[]): boolean {\n return tasks.some((task) => task.status !== 'deleted' && Boolean(task.blockedBy?.length));\n}\n\nexport function hasActiveWork(tasks: readonly Task[]): boolean {\n return tasks.some((task) => task.status === 'in_progress');\n}\n\nexport interface OverlayLayout {\n visible: Task[];\n hiddenCompleted: number;\n truncatedTail: number;\n}\n\n/**\n * Choose which rows fit the overlay budget.\n *\n * Active work wins limited space: in-progress first, then failed, blocked, and\n * pending. Selected rows are always returned in source order, however, so a\n * status update does not reshuffle an otherwise unchanged overlay.\n */\nexport function selectOverlayLayoutFromProjection(projection: TaskProjection, budget: number): OverlayLayout {\n const { groups, visible: sourceOrder } = projection;\n const active = [...groups.inProgress, ...groups.failed, ...groups.blocked, ...groups.pending];\n if (budget <= 0) {\n return {\n visible: [],\n hiddenCompleted: groups.completed.length,\n truncatedTail: active.length,\n };\n }\n if (sourceOrder.length <= budget) {\n return { visible: sourceOrder, hiddenCompleted: 0, truncatedTail: 0 };\n }\n\n if (active.length <= budget) {\n const remaining = budget - active.length;\n const shownCompleted = groups.completed.slice(0, remaining);\n const selected = new Set([...active, ...shownCompleted]);\n return {\n visible: sourceOrder.filter((task) => selected.has(task)),\n hiddenCompleted: groups.completed.length - shownCompleted.length,\n truncatedTail: 0,\n };\n }\n\n const selected = new Set(active.slice(0, budget));\n return {\n visible: sourceOrder.filter((task) => selected.has(task)),\n hiddenCompleted: groups.completed.length,\n truncatedTail: active.length - budget,\n };\n}\n\nexport function selectOverlayLayout(tasks: readonly Task[], budget: number): OverlayLayout {\n return selectOverlayLayoutFromProjection(deriveTaskProjection(tasks), budget);\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/doompi-task",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.16",
|
|
4
4
|
"description": "Pi task tracking extension with file-backed tasks and subagent delegation",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai",
|
|
7
|
+
"coding-agent",
|
|
8
|
+
"developer-tools",
|
|
9
|
+
"doompi",
|
|
10
|
+
"pi-package"
|
|
11
|
+
],
|
|
5
12
|
"homepage": "https://agimon.ai",
|
|
6
13
|
"license": "MIT",
|
|
7
14
|
"author": "Vuong Ngo",
|
|
@@ -142,13 +149,13 @@
|
|
|
142
149
|
},
|
|
143
150
|
"dependencies": {
|
|
144
151
|
"typebox": "1.1.38",
|
|
145
|
-
"@agimon-ai/doompi-
|
|
146
|
-
"@agimon-ai/doompi-
|
|
147
|
-
"@agimon-ai/doompi-ui": "0.0.1-alpha.
|
|
152
|
+
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.16",
|
|
153
|
+
"@agimon-ai/doompi-telemetry": "0.0.1-alpha.16",
|
|
154
|
+
"@agimon-ai/doompi-ui": "0.0.1-alpha.16"
|
|
148
155
|
},
|
|
149
156
|
"devDependencies": {
|
|
150
|
-
"@earendil-works/pi-coding-agent": "0.84.
|
|
151
|
-
"@earendil-works/pi-tui": "0.84.
|
|
157
|
+
"@earendil-works/pi-coding-agent": "0.84.2",
|
|
158
|
+
"@earendil-works/pi-tui": "0.84.2",
|
|
152
159
|
"@types/node": "26.1.1",
|
|
153
160
|
"@vitest/coverage-v8": "4.1.10",
|
|
154
161
|
"tsdown": "0.22.14",
|
|
@@ -156,8 +163,8 @@
|
|
|
156
163
|
"vitest": "4.1.10"
|
|
157
164
|
},
|
|
158
165
|
"peerDependencies": {
|
|
159
|
-
"@earendil-works/pi-coding-agent": "0.84.
|
|
160
|
-
"@earendil-works/pi-tui": "0.84.
|
|
166
|
+
"@earendil-works/pi-coding-agent": "0.84.2",
|
|
167
|
+
"@earendil-works/pi-tui": "0.84.2"
|
|
161
168
|
},
|
|
162
169
|
"peerDependenciesMeta": {
|
|
163
170
|
"@earendil-works/pi-coding-agent": {
|
|
@@ -172,7 +179,7 @@
|
|
|
172
179
|
},
|
|
173
180
|
"pi": {
|
|
174
181
|
"extensions": [
|
|
175
|
-
"./dist/extensions/
|
|
182
|
+
"./dist/extensions/doom.mjs"
|
|
176
183
|
]
|
|
177
184
|
},
|
|
178
185
|
"scripts": {
|