@agimon-ai/doompi-ui 0.0.1-alpha.0
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/LICENSE +21 -0
- package/README.md +41 -0
- package/dist/_virtual/_rolldown/runtime.cjs +1 -0
- package/dist/_virtual/_rolldown/runtime.mjs +1 -0
- package/dist/components/configOverlay.cjs +0 -0
- package/dist/components/configOverlay.cjs.map +1 -0
- package/dist/components/configOverlay.d.cts +0 -0
- package/dist/components/configOverlay.d.cts.map +1 -0
- package/dist/components/configOverlay.d.mts +0 -0
- package/dist/components/configOverlay.d.mts.map +1 -0
- package/dist/components/configOverlay.mjs +0 -0
- package/dist/components/configOverlay.mjs.map +1 -0
- package/dist/components/doomEditor.cjs +2 -0
- package/dist/components/doomEditor.cjs.map +1 -0
- package/dist/components/doomEditor.d.cts +59 -0
- package/dist/components/doomEditor.d.cts.map +1 -0
- package/dist/components/doomEditor.d.mts +59 -0
- package/dist/components/doomEditor.d.mts.map +1 -0
- package/dist/components/doomEditor.mjs +2 -0
- package/dist/components/doomEditor.mjs.map +1 -0
- package/dist/components/doomFooter.cjs +2 -0
- package/dist/components/doomFooter.cjs.map +1 -0
- package/dist/components/doomFooter.d.cts +32 -0
- package/dist/components/doomFooter.d.cts.map +1 -0
- package/dist/components/doomFooter.d.mts +32 -0
- package/dist/components/doomFooter.d.mts.map +1 -0
- package/dist/components/doomFooter.mjs +2 -0
- package/dist/components/doomFooter.mjs.map +1 -0
- package/dist/components/doomHeader.cjs +2 -0
- package/dist/components/doomHeader.cjs.map +1 -0
- package/dist/components/doomHeader.d.cts +34 -0
- package/dist/components/doomHeader.d.cts.map +1 -0
- package/dist/components/doomHeader.d.mts +34 -0
- package/dist/components/doomHeader.d.mts.map +1 -0
- package/dist/components/doomHeader.mjs +2 -0
- package/dist/components/doomHeader.mjs.map +1 -0
- package/dist/components/doomOverlay.cjs +2 -0
- package/dist/components/doomOverlay.cjs.map +1 -0
- package/dist/components/doomOverlay.d.cts +79 -0
- package/dist/components/doomOverlay.d.cts.map +1 -0
- package/dist/components/doomOverlay.d.mts +79 -0
- package/dist/components/doomOverlay.d.mts.map +1 -0
- package/dist/components/doomOverlay.mjs +2 -0
- package/dist/components/doomOverlay.mjs.map +1 -0
- package/dist/components/leaderHints.cjs +2 -0
- package/dist/components/leaderHints.cjs.map +1 -0
- package/dist/components/leaderHints.d.cts +37 -0
- package/dist/components/leaderHints.d.cts.map +1 -0
- package/dist/components/leaderHints.d.mts +37 -0
- package/dist/components/leaderHints.d.mts.map +1 -0
- package/dist/components/leaderHints.mjs +2 -0
- package/dist/components/leaderHints.mjs.map +1 -0
- package/dist/components/toolsOverlay.cjs +2 -0
- package/dist/components/toolsOverlay.cjs.map +1 -0
- package/dist/components/toolsOverlay.d.cts +29 -0
- package/dist/components/toolsOverlay.d.cts.map +1 -0
- package/dist/components/toolsOverlay.d.mts +29 -0
- package/dist/components/toolsOverlay.d.mts.map +1 -0
- package/dist/components/toolsOverlay.mjs +2 -0
- package/dist/components/toolsOverlay.mjs.map +1 -0
- package/dist/config.cjs +2 -0
- package/dist/config.cjs.map +1 -0
- package/dist/config.d.cts +5 -0
- package/dist/config.d.cts.map +1 -0
- package/dist/config.d.mts +5 -0
- package/dist/config.d.mts.map +1 -0
- package/dist/config.mjs +2 -0
- package/dist/config.mjs.map +1 -0
- package/dist/extension.cjs +2 -0
- package/dist/extension.cjs.map +1 -0
- package/dist/extension.d.cts +7 -0
- package/dist/extension.d.cts.map +1 -0
- package/dist/extension.d.mts +7 -0
- package/dist/extension.d.mts.map +1 -0
- package/dist/extension.mjs +2 -0
- package/dist/extension.mjs.map +1 -0
- package/dist/extensionName.cjs +2 -0
- package/dist/extensionName.cjs.map +1 -0
- package/dist/extensionName.d.cts +5 -0
- package/dist/extensionName.d.cts.map +1 -0
- package/dist/extensionName.d.mts +5 -0
- package/dist/extensionName.d.mts.map +1 -0
- package/dist/extensionName.mjs +2 -0
- package/dist/extensionName.mjs.map +1 -0
- package/dist/extensions/pi.cjs +2 -0
- package/dist/extensions/pi.cjs.map +1 -0
- package/dist/extensions/pi.d.cts +2 -0
- package/dist/extensions/pi.d.mts +2 -0
- package/dist/extensions/pi.mjs +2 -0
- package/dist/extensions/pi.mjs.map +1 -0
- package/dist/footer.cjs +2 -0
- package/dist/footer.cjs.map +1 -0
- package/dist/footer.d.cts +5 -0
- package/dist/footer.d.cts.map +1 -0
- package/dist/footer.d.mts +5 -0
- package/dist/footer.d.mts.map +1 -0
- package/dist/footer.mjs +2 -0
- package/dist/footer.mjs.map +1 -0
- package/dist/harnessMetadata.cjs +2 -0
- package/dist/harnessMetadata.cjs.map +1 -0
- package/dist/harnessMetadata.d.cts +21 -0
- package/dist/harnessMetadata.d.cts.map +1 -0
- package/dist/harnessMetadata.d.mts +21 -0
- package/dist/harnessMetadata.d.mts.map +1 -0
- package/dist/harnessMetadata.mjs +2 -0
- package/dist/harnessMetadata.mjs.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +25 -0
- package/dist/index.d.mts +25 -0
- package/dist/index.mjs +1 -0
- package/dist/leader.cjs +1 -0
- package/dist/leader.d.cts +2 -0
- package/dist/leader.d.mts +2 -0
- package/dist/leader.mjs +1 -0
- package/dist/leaderRegistry.cjs +2 -0
- package/dist/leaderRegistry.cjs.map +1 -0
- package/dist/leaderRegistry.d.cts +52 -0
- package/dist/leaderRegistry.d.cts.map +1 -0
- package/dist/leaderRegistry.d.mts +52 -0
- package/dist/leaderRegistry.d.mts.map +1 -0
- package/dist/leaderRegistry.mjs +2 -0
- package/dist/leaderRegistry.mjs.map +1 -0
- package/dist/logSinkTelemetry.cjs +2 -0
- package/dist/logSinkTelemetry.cjs.map +1 -0
- package/dist/logSinkTelemetry.d.cts +29 -0
- package/dist/logSinkTelemetry.d.cts.map +1 -0
- package/dist/logSinkTelemetry.d.mts +29 -0
- package/dist/logSinkTelemetry.d.mts.map +1 -0
- package/dist/logSinkTelemetry.mjs +2 -0
- package/dist/logSinkTelemetry.mjs.map +1 -0
- package/dist/mode.cjs +2 -0
- package/dist/mode.cjs.map +1 -0
- package/dist/mode.d.cts +5 -0
- package/dist/mode.d.cts.map +1 -0
- package/dist/mode.d.mts +5 -0
- package/dist/mode.d.mts.map +1 -0
- package/dist/mode.mjs +2 -0
- package/dist/mode.mjs.map +1 -0
- package/dist/rendering.cjs +2 -0
- package/dist/rendering.cjs.map +1 -0
- package/dist/rendering.d.cts +11 -0
- package/dist/rendering.d.cts.map +1 -0
- package/dist/rendering.d.mts +11 -0
- package/dist/rendering.d.mts.map +1 -0
- package/dist/rendering.mjs +2 -0
- package/dist/rendering.mjs.map +1 -0
- package/dist/skills.cjs +1 -0
- package/dist/skills.d.cts +1 -0
- package/dist/skills.d.mts +1 -0
- package/dist/skills.mjs +1 -0
- package/dist/theme.cjs +2 -0
- package/dist/theme.cjs.map +1 -0
- package/dist/theme.d.cts +95 -0
- package/dist/theme.d.cts.map +1 -0
- package/dist/theme.d.mts +95 -0
- package/dist/theme.d.mts.map +1 -0
- package/dist/theme.mjs +2 -0
- package/dist/theme.mjs.map +1 -0
- package/dist/toolInventory.cjs +2 -0
- package/dist/toolInventory.cjs.map +1 -0
- package/dist/toolInventory.d.cts +40 -0
- package/dist/toolInventory.d.cts.map +1 -0
- package/dist/toolInventory.d.mts +40 -0
- package/dist/toolInventory.d.mts.map +1 -0
- package/dist/toolInventory.mjs +2 -0
- package/dist/toolInventory.mjs.map +1 -0
- package/dist/uiState.cjs +2 -0
- package/dist/uiState.cjs.map +1 -0
- package/dist/uiState.d.cts +37 -0
- package/dist/uiState.d.cts.map +1 -0
- package/dist/uiState.d.mts +37 -0
- package/dist/uiState.d.mts.map +1 -0
- package/dist/uiState.mjs +2 -0
- package/dist/uiState.mjs.map +1 -0
- package/package.json +171 -0
- package/themes/doom-pi-dark.json +86 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { mode_d_exports } from "../mode.mjs";
|
|
2
|
+
import { LeaderSnapshot } from "../uiState.mjs";
|
|
3
|
+
import { ExtensionContext, ReadonlyFooterDataProvider, Theme } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { Component } from "@earendil-works/pi-tui";
|
|
5
|
+
//#region src/components/leaderHints.d.ts
|
|
6
|
+
declare class LeaderHints implements Component {
|
|
7
|
+
private readonly theme;
|
|
8
|
+
private readonly snapshot;
|
|
9
|
+
private readonly context?;
|
|
10
|
+
private readonly footerData?;
|
|
11
|
+
private readonly modes;
|
|
12
|
+
constructor(theme: Theme, snapshot: LeaderSnapshot, context?: ExtensionContext | undefined, footerData?: ReadonlyFooterDataProvider | undefined, modes?: readonly mode_d_exports.DoomModeStatus[]);
|
|
13
|
+
invalidate(): void;
|
|
14
|
+
render(width: number): string[];
|
|
15
|
+
/**
|
|
16
|
+
* Lays options out on a fixed cell grid rather than a packed run of segments,
|
|
17
|
+
* so keys and labels stay column-aligned when the list wraps.
|
|
18
|
+
*/
|
|
19
|
+
private renderOptionGrid;
|
|
20
|
+
/** Content lines only: the caller paints the shade and pads to full width. */
|
|
21
|
+
private renderDiagnostics;
|
|
22
|
+
private diagnosticLine;
|
|
23
|
+
/**
|
|
24
|
+
* The enabled modes with their detail, which is the long form of what the
|
|
25
|
+
* editor's badge row shows as bare names.
|
|
26
|
+
*
|
|
27
|
+
* This panel is opened deliberately and has room, so it is the right place
|
|
28
|
+
* for `plan (normal - read only)`: the badge row is glanced at mid-typing and
|
|
29
|
+
* only has to answer which modes are on.
|
|
30
|
+
*/
|
|
31
|
+
private modeDiagnostics;
|
|
32
|
+
private sessionDiagnostics;
|
|
33
|
+
private extensionDiagnostics;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { LeaderHints };
|
|
37
|
+
//# sourceMappingURL=leaderHints.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leaderHints.d.mts","names":[],"sources":["../../src/components/leaderHints.ts"],"mappings":";;;;;cAgDa,uBAAuB;mBAEf;mBACA;mBACA;mBACA;mBACA;EAJA,YAAA,OAAO,OACP,UAAU,gBACV,UAAU,8BACV,aAAa,wCACb,iBAAgB,eAAA;EAGnC;EAEA,OAAO;;;;;UA6CC;;UA+BA;UA8BA;;;;;;;;;UAYA;UAUA;UAoCA"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{alignLine as e,fitLine as t,formatTokens as n,packSegments as r,padLine as i}from"../rendering.mjs";import{visibleWidth as a}from"@earendil-works/pi-tui";const o=` · `,s=` `,c=`text`,l=`accent`,u=`warning`,d=`mdHeading`,f=[`mdCode`,`toolDiffAdded`,`warning`,`muted`];var p=class{theme;snapshot;context;footerData;modes;constructor(e,t,n,r,i=[]){this.theme=e,this.snapshot=t,this.context=n,this.footerData=r,this.modes=i}invalidate(){}render(t){if(t<=0||!this.snapshot.active)return[];if(t<4)return[this.theme.fg(`borderAccent`,`─`.repeat(t))];let n=Math.max(1,t-4),r=e=>this.theme.bg(`userMessageBg`,i(` ${e}`,t)),a=this.theme.inverse(this.theme.bold(this.theme.fg(d,` LEADER `))),o=`${this.snapshot.prefix.join(` › `)} / ${this.snapshot.label}`,s=r(e(`${a} ${this.theme.bold(this.theme.fg(c,o))}`,this.theme.fg(`dim`,`esc cancel · backspace up`),n)),l=this.snapshot.rootOptions?.map(e=>`${e.key} ${e.label}`).join(` · `),u=t>=100&&this.snapshot.prefix.length>1&&l?[r(this.theme.fg(`dim`,`root ${l}`))]:[],f=Math.max(1,14-u.length),p=this.renderOptionGrid(n).slice(0,f).map(r),m=Math.max(0,12-u.length-p.length),h=this.renderDiagnostics(n).slice(0,m).map(r);return[...[r(``),s,r(``),...p,...u,...h].slice(0,15),r(``)]}renderOptionGrid(e){if(this.snapshot.options.length===0)return[];let n=this.snapshot.options.map(e=>({heading:`${this.theme.inverse(this.theme.bold(this.theme.fg(l,` ${e.key} `)))} ${this.theme.bold(this.theme.fg(c,e.label))}`,headingWidth:4+a(e.label),detail:e.detail?`${s}${this.theme.fg(`dim`,e.detail)}`:``,detailWidth:e.detail?a(s)+a(e.detail):0})),r=Math.max(...n.map(e=>Math.max(e.headingWidth,e.detailWidth))),o=Math.min(e,r),u=Math.max(1,Math.floor((e+2)/(o+2))),d=` `.repeat(2),f=[];for(let r=0;r<n.length;r+=u){let a=n.slice(r,r+u);f.push(t(a.map(e=>i(e.heading,o)).join(d),e)),f.push(t(a.map(e=>i(e.detail,o)).join(d),e))}return f}renderDiagnostics(e){let t=this.sessionDiagnostics(),n=this.extensionDiagnostics(e),r=this.modeDiagnostics();if(!t&&!r&&n.length===0)return[];let i=[this.theme.fg(`borderMuted`,`─`.repeat(e))];r&&i.push(this.diagnosticLine(`MODES`,r,u)),t&&i.push(this.diagnosticLine(`SESSION`,t,d));for(let[e,t]of n.entries()){let n=e===0;i.push(this.diagnosticLine(n?`EXTENSIONS`:`↳`,t,n?l:`dim`))}return i}diagnosticLine(e,t,n){return`${this.theme.bold(this.theme.fg(n,e.padEnd(11)))}${t}`}modeDiagnostics(){if(this.modes.length!==0)return this.modes.map(e=>{let t=this.theme.fg(u,e.label.toLowerCase());return e.detail?`${t}${this.theme.fg(`dim`,` (${e.detail})`)}`:t}).join(this.theme.fg(`dim`,o))}sessionDiagnostics(){if(!this.context)return;let e=0,t=0,r=0,i=0,a=0;for(let n of this.context.sessionManager.getBranch())n.type===`message`&&n.message.role===`assistant`&&(e+=n.message.usage.input,t+=n.message.usage.output,r+=n.message.usage.cacheRead,i+=n.message.usage.cacheWrite,a+=n.message.usage.cost.total);let s=e+r+i,c=s===0?0:r/s*100,l=this.context.getContextUsage(),d=l?.percent===null||l?.percent===void 0?`ctx ?`:`ctx ${l.percent.toFixed(1)}% / ${n(l.contextWindow)}`;return`${this.theme.fg(`muted`,`↑${n(e)} ↓${n(t)} R${n(r)} W${n(i)} CH${c.toFixed(1)}%`)} ${this.theme.fg(u,`$${a.toFixed(3)}`)}${this.theme.fg(`dim`,`${o}${d}`)}`}extensionDiagnostics(e){if(!this.footerData)return[];let t=[...this.footerData.getExtensionStatuses().values()],n=this.footerData.getAvailableProviderCount(),i=[...t];n>0&&i.push(`${n} provider${n===1?``:`s`}`);let a=i.map((e,t)=>this.theme.fg(f[t%f.length],e)),s=Math.max(1,e-15);return r(a,s,this.theme.fg(`dim`,o)).slice(0,2)}};export{p as LeaderHints};
|
|
2
|
+
//# sourceMappingURL=leaderHints.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leaderHints.mjs","names":[],"sources":["../../src/components/leaderHints.ts"],"sourcesContent":["import type { ExtensionContext, ReadonlyFooterDataProvider, Theme, ThemeColor } from '@earendil-works/pi-coding-agent';\n\nimport { visibleWidth, type Component } from '@earendil-works/pi-tui';\nimport { alignLine, fitLine, formatTokens, packSegments, padLine } from '../rendering.ts';\nimport type { DoomModeStatus } from '../mode.ts';\nimport type { LeaderSnapshot } from '../uiState.ts';\n\n/** The background palette is not exported by name, so take it from the method. */\ntype ThemeBg = Parameters<Theme['bg']>[0];\n\nconst WIDE_HINT_WIDTH = 100;\n/**\n * No frame rows to pay for, so the whole budget is content. Two-line option\n * cards need roughly double the grid rows a single-line list did, plus a blank\n * row of breathing space at the top and bottom.\n */\nconst MAX_WIDGET_LINES = 16;\n/** Leading pad inside the shaded block, mirrored by padLine on the right. */\nconst PANEL_INSET = ' ';\nconst MIN_RENDER_WIDTH = 4;\nconst CONTENT_INSET = 4;\nconst GRID_GAP = 2;\nconst DIAGNOSTIC_LABEL_WIDTH = 11;\nconst DIAGNOSTIC_VALUE_INSET = 15;\nconst MAX_EXTENSION_LINES = 2;\nconst STATUS_SEPARATOR = ' · ';\nconst SEGMENT_GAP = ' ';\n/** The inverse key badge is ` k `, three columns wide. */\nconst KEY_BADGE_WIDTH = 3;\n/** Puts the description under the label rather than under the badge. */\nconst DETAIL_INDENT = ' ';\nconst HORIZONTAL_BORDER = '─';\nconst CONTINUATION_MARKER = '↳';\nconst DIM_COLOR: ThemeColor = 'dim';\nconst TEXT_COLOR: ThemeColor = 'text';\nconst MUTED_COLOR: ThemeColor = 'muted';\nconst ACCENT_COLOR: ThemeColor = 'accent';\nconst WARNING_COLOR: ThemeColor = 'warning';\nconst HEADING_COLOR: ThemeColor = 'mdHeading';\nconst BORDER_COLOR: ThemeColor = 'borderAccent';\nconst MUTED_BORDER_COLOR: ThemeColor = 'borderMuted';\n/** Raised surface tone: one step above the terminal background, no frame needed. */\nconst PANEL_BACKGROUND: ThemeBg = 'userMessageBg';\n// The mockup colors each extension status differently so adjacent values stay\n// separable at a glance; cycling this palette reproduces that without hardcoding\n// a color per status key.\nconst STATUS_COLORS: ThemeColor[] = ['mdCode', 'toolDiffAdded', 'warning', 'muted'];\n\nexport class LeaderHints implements Component {\n constructor(\n private readonly theme: Theme,\n private readonly snapshot: LeaderSnapshot,\n private readonly context?: ExtensionContext,\n private readonly footerData?: ReadonlyFooterDataProvider,\n private readonly modes: readonly DoomModeStatus[] = [],\n ) {}\n\n invalidate(): void {}\n\n render(width: number): string[] {\n if (width <= 0 || !this.snapshot.active) return [];\n if (width < MIN_RENDER_WIDTH) return [this.theme.fg(BORDER_COLOR, HORIZONTAL_BORDER.repeat(width))];\n\n // A filled block rather than a framed one: the shade alone separates the\n // panel from the transcript, which buys back the two rows a frame costs.\n const contentWidth = Math.max(1, width - CONTENT_INSET);\n const shade = (content: string): string =>\n this.theme.bg(PANEL_BACKGROUND, padLine(`${PANEL_INSET}${content}`, width));\n const badge = this.theme.inverse(this.theme.bold(this.theme.fg(HEADING_COLOR, ' LEADER ')));\n const breadcrumb = `${this.snapshot.prefix.join(' › ')} / ${this.snapshot.label}`;\n const header = shade(\n alignLine(\n `${badge} ${this.theme.bold(this.theme.fg(TEXT_COLOR, breadcrumb))}`,\n this.theme.fg(DIM_COLOR, 'esc cancel · backspace up'),\n contentWidth,\n ),\n );\n const rootTrail = this.snapshot.rootOptions?.map((option) => `${option.key} ${option.label}`).join(' · ');\n const trail =\n width >= WIDE_HINT_WIDTH && this.snapshot.prefix.length > 1 && rootTrail\n ? [shade(this.theme.fg(DIM_COLOR, `root ${rootTrail}`))]\n : [];\n // Options are the interactive surface, so they claim space before the\n // diagnostics do; truncating them would hide a reachable key.\n const optionBudget = Math.max(1, MAX_WIDGET_LINES - 2 - trail.length);\n const optionLines = this.renderOptionGrid(contentWidth).slice(0, optionBudget).map(shade);\n // Four rows are spoken for before diagnostics get any: the blank, header and\n // blank above, and the closing blank below. Counting only two of them let\n // this budget overrun, and the final `slice` did the real cutting silently\n // from the end, which is how the EXTENSIONS row went missing when MODES\n // arrived. Take all four off here so what is dropped is decided in one place.\n const diagnosticsBudget = Math.max(0, MAX_WIDGET_LINES - 4 - trail.length - optionLines.length);\n const diagnostics = this.renderDiagnostics(contentWidth).slice(0, diagnosticsBudget).map(shade);\n // Blank shaded rows top and bottom: the panel butts against the editor above\n // and the modeline below, so the shade needs room before its first glyph.\n const body = [shade(''), header, shade(''), ...optionLines, ...trail, ...diagnostics];\n\n return [...body.slice(0, MAX_WIDGET_LINES - 1), shade('')];\n }\n\n /**\n * Lays options out on a fixed cell grid rather than a packed run of segments,\n * so keys and labels stay column-aligned when the list wraps.\n */\n private renderOptionGrid(width: number): string[] {\n if (this.snapshot.options.length === 0) return [];\n\n // Two lines per option, as the mockup lays them out: the key badge and a\n // bold label, then the description indented under the label.\n const cells = this.snapshot.options.map((option) => {\n const key = this.theme.inverse(this.theme.bold(this.theme.fg(ACCENT_COLOR, ` ${option.key} `)));\n return {\n heading: `${key} ${this.theme.bold(this.theme.fg(TEXT_COLOR, option.label))}`,\n headingWidth: KEY_BADGE_WIDTH + 1 + visibleWidth(option.label),\n detail: option.detail ? `${DETAIL_INDENT}${this.theme.fg(DIM_COLOR, option.detail)}` : '',\n detailWidth: option.detail ? visibleWidth(DETAIL_INDENT) + visibleWidth(option.detail) : 0,\n };\n });\n const widest = Math.max(...cells.map((cell) => Math.max(cell.headingWidth, cell.detailWidth)));\n const cellWidth = Math.min(width, widest);\n const columns = Math.max(1, Math.floor((width + GRID_GAP) / (cellWidth + GRID_GAP)));\n const gap = ' '.repeat(GRID_GAP);\n const rows: string[] = [];\n for (let index = 0; index < cells.length; index += columns) {\n const group = cells.slice(index, index + columns);\n rows.push(fitLine(group.map((cell) => padLine(cell.heading, cellWidth)).join(gap), width));\n // Always two rows per card, even where no option carries a description:\n // dropping the empty one lets the next row's badges close up against the\n // ones above, which reads as a vertical block of keys rather than cards.\n rows.push(fitLine(group.map((cell) => padLine(cell.detail, cellWidth)).join(gap), width));\n }\n return rows;\n }\n\n /** Content lines only: the caller paints the shade and pads to full width. */\n private renderDiagnostics(width: number): string[] {\n const session = this.sessionDiagnostics();\n const extensions = this.extensionDiagnostics(width);\n const modes = this.modeDiagnostics();\n if (!session && !modes && extensions.length === 0) return [];\n\n const lines = [this.theme.fg(MUTED_BORDER_COLOR, HORIZONTAL_BORDER.repeat(width))];\n // Above SESSION because it is the more consequential fact: session counters\n // describe what this session has spent, a mode describes what it will do.\n //\n // No blank rows between these three. The diagnostics budget is what is left\n // after the option grid takes its share, which at a full grid is exactly one\n // row per label: a spacer here does not cost breathing room, it costs the\n // EXTENSIONS row entirely. Distinct labels in distinct colours separate them\n // well enough without one.\n if (modes) lines.push(this.diagnosticLine('MODES', modes, WARNING_COLOR));\n if (session) lines.push(this.diagnosticLine('SESSION', session, HEADING_COLOR));\n for (const [index, extension] of extensions.entries()) {\n const isFirst = index === 0;\n lines.push(\n this.diagnosticLine(\n isFirst ? 'EXTENSIONS' : CONTINUATION_MARKER,\n extension,\n isFirst ? ACCENT_COLOR : DIM_COLOR,\n ),\n );\n }\n return lines;\n }\n\n private diagnosticLine(label: string, value: string, color: ThemeColor): string {\n return `${this.theme.bold(this.theme.fg(color, label.padEnd(DIAGNOSTIC_LABEL_WIDTH)))}${value}`;\n }\n\n /**\n * The enabled modes with their detail, which is the long form of what the\n * editor's badge row shows as bare names.\n *\n * This panel is opened deliberately and has room, so it is the right place\n * for `plan (normal - read only)`: the badge row is glanced at mid-typing and\n * only has to answer which modes are on.\n */\n private modeDiagnostics(): string | undefined {\n if (this.modes.length === 0) return undefined;\n return this.modes\n .map((mode) => {\n const label = this.theme.fg(WARNING_COLOR, mode.label.toLowerCase());\n return mode.detail ? `${label}${this.theme.fg(DIM_COLOR, ` (${mode.detail})`)}` : label;\n })\n .join(this.theme.fg(DIM_COLOR, STATUS_SEPARATOR));\n }\n\n private sessionDiagnostics(): string | undefined {\n if (!this.context) return undefined;\n\n let input = 0;\n let output = 0;\n let cacheRead = 0;\n let cacheWrite = 0;\n let cost = 0;\n for (const entry of this.context.sessionManager.getBranch()) {\n if (entry.type !== 'message' || entry.message.role !== 'assistant') continue;\n input += entry.message.usage.input;\n output += entry.message.usage.output;\n cacheRead += entry.message.usage.cacheRead;\n cacheWrite += entry.message.usage.cacheWrite;\n cost += entry.message.usage.cost.total;\n }\n\n const cachedTotal = input + cacheRead + cacheWrite;\n const cacheHit = cachedTotal === 0 ? 0 : (cacheRead / cachedTotal) * 100;\n const usage = this.context.getContextUsage();\n const context =\n usage?.percent === null || usage?.percent === undefined\n ? 'ctx ?'\n : `ctx ${usage.percent.toFixed(1)}% / ${formatTokens(usage.contextWindow)}`;\n // Built as sibling segments rather than nested theme calls so each color\n // closes cleanly instead of resetting the one around it.\n const metrics = this.theme.fg(\n MUTED_COLOR,\n `↑${formatTokens(input)} ↓${formatTokens(output)} R${formatTokens(cacheRead)} W${formatTokens(\n cacheWrite,\n )} CH${cacheHit.toFixed(1)}%`,\n );\n const costText = this.theme.fg(WARNING_COLOR, `$${cost.toFixed(3)}`);\n return `${metrics}${SEGMENT_GAP}${costText}${this.theme.fg(DIM_COLOR, `${STATUS_SEPARATOR}${context}`)}`;\n }\n\n private extensionDiagnostics(width: number): string[] {\n if (!this.footerData) return [];\n\n const statuses = [...this.footerData.getExtensionStatuses().values()];\n const providers = this.footerData.getAvailableProviderCount();\n const values = [...statuses];\n if (providers > 0) values.push(`${providers} provider${providers === 1 ? '' : 's'}`);\n const colored = values.map((value, index) =>\n this.theme.fg(STATUS_COLORS[index % STATUS_COLORS.length] as ThemeColor, value),\n );\n const valueWidth = Math.max(1, width - DIAGNOSTIC_VALUE_INSET);\n return packSegments(colored, valueWidth, this.theme.fg(DIM_COLOR, STATUS_SEPARATOR)).slice(0, MAX_EXTENSION_LINES);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./doomOverlay.cjs");let t=require("@earendil-works/pi-tui");const n=[[`↑↓`,`move`],[`esc`,`close`]],r=`No tools are registered in this session.`;function i(e,n){let r=(0,t.truncateToWidth)(e,Math.max(0,n),`…`);return r+` `.repeat(Math.max(0,n-(0,t.visibleWidth)(r)))}function a(e,n,r){let a=(0,t.visibleWidth)(n),o=Math.max(0,r-a-1);return i(e,o)+` `.repeat(Math.max(1,r-o-a))+i(n,a)}function o(e,t){if(t<=0)return[];let n=[],r=``;for(let i of e.split(/\s+/).filter(Boolean))r.length===0?r=i:r.length+1+i.length<=t?r+=` ${i}`:(n.push(r),r=i);return r.length>0&&n.push(r),n}function s(e){if(typeof e!=`object`||!e)return[];let t=e;if(typeof t.properties!=`object`||t.properties===null)return[];let n=new Set(Array.isArray(t.required)?t.required.filter(e=>typeof e==`string`):[]);return Object.entries(t.properties).map(([e,t])=>{let r=typeof t==`object`&&t?t.type:void 0;return{name:e,type:typeof r==`string`?r:``,required:n.has(e)}})}function c(e){return e.tools.reduce((e,t)=>e+ +!!t.active,0)}var l=class extends e.DoomOverlay{sources;done;selected;bodyHeight=1;constructor(e,t,n,r){super(e,t),this.sources=n,this.done=r,this.selected=this.rows().findIndex(e=>e.kind===`tool`)}rows(){let e=[];for(let t of this.sources){e.push({kind:`source`,source:t});for(let n of t.tools)e.push({kind:`tool`,source:t,tool:n})}return e}currentTool(){let e=this.rows()[this.selected];return e?.kind===`tool`?e:void 0}move(e){let t=this.rows(),n=e<0?-1:1,r=this.selected;for(let i=Math.abs(e);i>0;--i){let e=r+n;for(;e>=0&&e<t.length&&t[e]?.kind!==`tool`;)e+=n;if(e<0||e>=t.length)break;r=e}r!==this.selected&&(this.selected=r,this.tui.requestRender())}handleInput(e){if((0,t.matchesKey)(e,`escape`)||(0,t.matchesKey)(e,`ctrl+c`)||e===`q`){this.done(void 0);return}if((0,t.matchesKey)(e,`up`)||e===`k`)return this.move(-1);if((0,t.matchesKey)(e,`down`)||e===`j`)return this.move(1);if((0,t.matchesKey)(e,`pageUp`))return this.move(-Math.max(1,this.bodyHeight-1));if((0,t.matchesKey)(e,`pageDown`))return this.move(Math.max(1,this.bodyHeight-1))}treeLines(e){let t=this.rows(),n=[this.theme.bold(`SOURCES`),``];if(t.length===0)return n.push(this.theme.fg(`dim`,r)),n;let o=Math.max(1,this.bodyHeight-n.length),s=Math.max(0,Math.min(this.selected,t.length-1)),l=Math.max(0,Math.min(s-o+1,Math.max(0,t.length-o)));for(let[r,u]of t.slice(l,l+o).entries()){let t=u.kind===`source`?a(this.theme.bold(u.source.label),this.theme.fg(`dim`,u.source.status??String(c(u.source))),e):this.theme.fg(u.tool.active?`text`:`dim`,i(` ${u.tool.name}`,e));n.push(l+r===s?this.theme.bg(`selectedBg`,i(t,e)):t)}return n}field(e,t,n){let r=Math.max(1,n-12);return o(t,r).map((t,n)=>`${this.theme.fg(`dim`,i(n===0?e:``,12))}${i(t,r)}`)}toolLines(e,t,n){let r=[this.theme.bold(t.name),``];r.push(...this.field(`source`,e.label,n)),e.detail&&r.push(...this.field(`registered`,e.detail,n)),r.push(...this.field(`status`,t.active?this.theme.fg(`success`,`active`):this.theme.fg(`dim`,`inactive`),n)),t.description&&(r.push(``),r.push(...o(t.description,n)));let i=s(t.parameters);if(i.length>0){r.push(``),r.push(this.theme.fg(`dim`,`PARAMETERS`));for(let e of i){let t=e.required?`${e.name}${this.theme.fg(`warning`,` *`)}`:e.name;r.push(a(t,this.theme.fg(`dim`,e.type),n))}}for(let e of t.promptGuidelines??[])r.push(``),r.push(...o(e,n));return r}detailLines(e){let t=this.currentTool();return t?this.toolLines(t.source,t.tool,e):[this.theme.fg(`dim`,r)]}getChrome(){let t=`${this.sources.reduce((e,t)=>e+c(t),0)}/${this.sources.reduce((e,t)=>e+t.tools.length,0)}`;return{title:`TOOLS`,accent:e.DOOM_OVERLAY_ACCENT,breadcrumb:`SPC › e / t · tools`,headerRight:`${t} tools · ${this.sources.length} sources · esc close`,footer:this.hintLine()}}hintLine(){return n.map(([e,t])=>`${this.theme.bg(`selectedBg`,this.theme.fg(`text`,` ${e} `))}${this.theme.fg(`dim`,` ${t}`)}`).join(this.theme.fg(`dim`,` `))}renderBody(e,n){if(this.bodyHeight=n,e<48)return this.renderStacked(e,n);let r=Math.max(18,Math.floor((e-1)*.3333333333333333)),a=Math.max(1,e-r-1),o=Math.max(1,r-1),s=Math.max(1,a-1),c=this.treeLines(o),l=this.detailLines(s),u=this.theme.fg(`borderMuted`,`│`);return Array.from({length:n},(n,r)=>{let a=`${i(c[r]??``,o)} ${u} ${i(l[r]??``,s)}`;return(0,t.truncateToWidth)(a,e,`…`)})}renderStacked(e,t){let n=Math.max(1,Math.floor((t-1)/2)),r=Math.max(0,t-n-1);this.bodyHeight=n;let i=this.treeLines(e),a=this.detailLines(e);return[...i.slice(0,n),...Array.from({length:Math.max(0,n-i.length)},()=>``),this.theme.fg(`borderMuted`,`─`.repeat(e)),...a.slice(0,r)]}};async function u(t,n){await t.ui.custom((e,t,r,i)=>new l(e,t,n,i),e.DOOM_FULLSCREEN_UI_OPTIONS)}exports.ToolsOverlayComponent=l,exports.openToolsOverlay=u;
|
|
2
|
+
//# sourceMappingURL=toolsOverlay.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolsOverlay.cjs","names":["truncateToWidth","visibleWidth","DoomOverlay","matchesKey","DOOM_OVERLAY_ACCENT","DOOM_FULLSCREEN_UI_OPTIONS"],"sources":["../../src/components/toolsOverlay.ts"],"sourcesContent":["/**\n * Tools browser: the `SPC e t` overlay.\n *\n * A grouped list on the left (Pi core, one group per MCP server, one per\n * extension) and a detail pane on the right for the tool under the cursor.\n * Read-only: nothing here enables or disables a tool, so it cannot fight plan\n * mode or the workflow dispatcher over the active tool set.\n *\n * Group headings are labels, not controls: everything is always expanded and\n * the cursor steps over them, so there is no expansion state to track and the\n * whole inventory is legible without navigating it. The tree stays a flat row\n * list, which keeps selection a single index and every row assertable as text.\n */\n\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\nimport type { ToolEntry, ToolSource } from '../toolInventory.ts';\nimport {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from './doomOverlay.ts';\n\ntype Row = { kind: 'source'; source: ToolSource } | { kind: 'tool'; source: ToolSource; tool: ToolEntry };\n\nconst TITLE = 'TOOLS';\nconst BREADCRUMB = 'SPC › e / t · tools';\nconst HEADER_RIGHT = 'esc close';\nconst HINTS: readonly (readonly [string, string])[] = [\n ['↑↓', 'move'],\n ['esc', 'close'],\n];\n\nconst MIN_WIDTH = 48;\n/** A third for the tree, two thirds for the tool being read. */\nconst TREE_PANE_RATIO = 1 / 3;\n/** One blank column each side of the pane divider so neither column touches it. */\nconst PANE_GUTTER = 1;\nconst MIN_PANE_WIDTH = 18;\nconst ELLIPSIS = '…';\nconst TOOL_INDENT = ' ';\nconst LABEL_COLUMN = 12;\nconst REQUIRED_MARKER = ' *';\nconst EMPTY_MESSAGE = 'No tools are registered in this session.';\nconst ACTIVE_LABEL = 'active';\nconst INACTIVE_LABEL = 'inactive';\nconst SOURCE_HEADING = 'SOURCES';\n\nfunction fit(text: string, width: number): string {\n const clipped = truncateToWidth(text, Math.max(0, width), ELLIPSIS);\n return clipped + ' '.repeat(Math.max(0, width - visibleWidth(clipped)));\n}\n\nfunction rightAligned(left: string, right: string, width: number): string {\n const rightWidth = visibleWidth(right);\n const leftWidth = Math.max(0, width - rightWidth - 1);\n return fit(left, leftWidth) + ' '.repeat(Math.max(1, width - leftWidth - rightWidth)) + fit(right, rightWidth);\n}\n\nfunction wrap(text: string, width: number): string[] {\n if (width <= 0) return [];\n const lines: string[] = [];\n let current = '';\n for (const word of text.split(/\\s+/).filter(Boolean)) {\n if (current.length === 0) current = word;\n else if (current.length + 1 + word.length <= width) current += ` ${word}`;\n else {\n lines.push(current);\n current = word;\n }\n }\n if (current.length > 0) lines.push(current);\n return lines;\n}\n\n/** Top-level properties of a JSON-schema-shaped parameter object, if it has any. */\nfunction parameterRows(parameters: unknown): readonly { name: string; type: string; required: boolean }[] {\n if (typeof parameters !== 'object' || parameters === null) return [];\n const schema = parameters as { properties?: unknown; required?: unknown };\n if (typeof schema.properties !== 'object' || schema.properties === null) return [];\n const required = new Set(\n Array.isArray(schema.required) ? schema.required.filter((name) => typeof name === 'string') : [],\n );\n return Object.entries(schema.properties as Record<string, unknown>).map(([name, value]) => {\n const type = typeof value === 'object' && value !== null ? (value as { type?: unknown }).type : undefined;\n return {\n name,\n type: typeof type === 'string' ? type : '',\n required: required.has(name),\n };\n });\n}\n\nfunction activeCount(source: ToolSource): number {\n return source.tools.reduce((total, tool) => total + (tool.active ? 1 : 0), 0);\n}\n\nexport class ToolsOverlayComponent extends DoomOverlay {\n private readonly sources: readonly ToolSource[];\n private readonly done: (result: undefined) => void;\n private selected: number;\n private bodyHeight = 1;\n\n constructor(tui: DoomOverlayTui, theme: Theme, sources: readonly ToolSource[], done: (result: undefined) => void) {\n super(tui, theme);\n this.sources = sources;\n this.done = done;\n this.selected = this.rows().findIndex((row) => row.kind === 'tool');\n }\n\n /** Every source is always expanded: the source row is a heading, not a control. */\n private rows(): readonly Row[] {\n const rows: Row[] = [];\n for (const source of this.sources) {\n rows.push({ kind: 'source', source });\n for (const tool of source.tools) rows.push({ kind: 'tool', source, tool });\n }\n return rows;\n }\n\n private currentTool(): Extract<Row, { kind: 'tool' }> | undefined {\n const row = this.rows()[this.selected];\n return row?.kind === 'tool' ? row : undefined;\n }\n\n /** Source rows are labels, so the cursor steps over them to the next tool. */\n private move(delta: number): void {\n const rows = this.rows();\n const step = delta < 0 ? -1 : 1;\n let index = this.selected;\n for (let remaining = Math.abs(delta); remaining > 0; remaining -= 1) {\n let next = index + step;\n while (next >= 0 && next < rows.length && rows[next]?.kind !== 'tool') next += step;\n if (next < 0 || next >= rows.length) break;\n index = next;\n }\n if (index === this.selected) return;\n this.selected = index;\n this.tui.requestRender();\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c') || data === 'q') {\n this.done(undefined);\n return;\n }\n if (matchesKey(data, 'up') || data === 'k') return this.move(-1);\n if (matchesKey(data, 'down') || data === 'j') return this.move(1);\n if (matchesKey(data, 'pageUp')) return this.move(-Math.max(1, this.bodyHeight - 1));\n if (matchesKey(data, 'pageDown')) return this.move(Math.max(1, this.bodyHeight - 1));\n }\n\n private treeLines(width: number): string[] {\n const rows = this.rows();\n const lines = [this.theme.bold(SOURCE_HEADING), ''];\n if (rows.length === 0) {\n lines.push(this.theme.fg('dim', EMPTY_MESSAGE));\n return lines;\n }\n\n const budget = Math.max(1, this.bodyHeight - lines.length);\n const selected = Math.max(0, Math.min(this.selected, rows.length - 1));\n const start = Math.max(0, Math.min(selected - budget + 1, Math.max(0, rows.length - budget)));\n for (const [offset, row] of rows.slice(start, start + budget).entries()) {\n const line =\n row.kind === 'source'\n ? rightAligned(\n this.theme.bold(row.source.label),\n this.theme.fg('dim', row.source.status ?? String(activeCount(row.source))),\n width,\n )\n : // Colour separates the active tool set from the merely registered one,\n // so the cursor never has to visit a row to learn its state.\n this.theme.fg(row.tool.active ? 'text' : 'dim', fit(`${TOOL_INDENT}${row.tool.name}`, width));\n lines.push(start + offset === selected ? this.theme.bg('selectedBg', fit(line, width)) : line);\n }\n return lines;\n }\n\n private field(label: string, value: string, width: number): string[] {\n const valueWidth = Math.max(1, width - LABEL_COLUMN);\n return wrap(value, valueWidth).map(\n (line, index) => `${this.theme.fg('dim', fit(index === 0 ? label : '', LABEL_COLUMN))}${fit(line, valueWidth)}`,\n );\n }\n\n private toolLines(source: ToolSource, tool: ToolEntry, width: number): string[] {\n const lines = [this.theme.bold(tool.name), ''];\n lines.push(...this.field('source', source.label, width));\n // Source rows no longer open a detail pane, so the entry path is shown here.\n if (source.detail) lines.push(...this.field('registered', source.detail, width));\n lines.push(\n ...this.field(\n 'status',\n tool.active ? this.theme.fg('success', ACTIVE_LABEL) : this.theme.fg('dim', INACTIVE_LABEL),\n width,\n ),\n );\n if (tool.description) {\n lines.push('');\n lines.push(...wrap(tool.description, width));\n }\n const parameters = parameterRows(tool.parameters);\n if (parameters.length > 0) {\n lines.push('');\n lines.push(this.theme.fg('dim', 'PARAMETERS'));\n for (const parameter of parameters) {\n // The name takes the whole line so long ones stop truncating into the\n // type, which sits flush right where the eye can run down the column.\n const name = parameter.required\n ? `${parameter.name}${this.theme.fg('warning', REQUIRED_MARKER)}`\n : parameter.name;\n lines.push(rightAligned(name, this.theme.fg('dim', parameter.type), width));\n }\n }\n for (const guideline of tool.promptGuidelines ?? []) {\n lines.push('');\n lines.push(...wrap(guideline, width));\n }\n return lines;\n }\n\n private detailLines(width: number): string[] {\n const row = this.currentTool();\n if (!row) return [this.theme.fg('dim', EMPTY_MESSAGE)];\n return this.toolLines(row.source, row.tool, width);\n }\n\n protected getChrome(): DoomOverlayChrome {\n // Active over registered: the first number is what the model can call, the\n // second is what a different mode or profile could turn back on.\n const active = this.sources.reduce((total, source) => total + activeCount(source), 0);\n const tools = `${active}/${this.sources.reduce((total, source) => total + source.tools.length, 0)}`;\n return {\n title: TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: BREADCRUMB,\n headerRight: `${tools} tools · ${this.sources.length} sources · ${HEADER_RIGHT}`,\n footer: this.hintLine(),\n };\n }\n\n private hintLine(): string {\n return HINTS.map(\n ([key, label]) =>\n `${this.theme.bg('selectedBg', this.theme.fg('text', ` ${key} `))}${this.theme.fg('dim', ` ${label}`)}`,\n ).join(this.theme.fg('dim', ' '));\n }\n\n protected renderBody(width: number, height: number): string[] {\n this.bodyHeight = height;\n if (width < MIN_WIDTH) return this.renderStacked(width, height);\n\n const leftWidth = Math.max(MIN_PANE_WIDTH, Math.floor((width - 1) * TREE_PANE_RATIO));\n const rightWidth = Math.max(1, width - leftWidth - 1);\n const leftContent = Math.max(1, leftWidth - PANE_GUTTER);\n const rightContent = Math.max(1, rightWidth - PANE_GUTTER);\n const left = this.treeLines(leftContent);\n const right = this.detailLines(rightContent);\n\n const divider = this.theme.fg('borderMuted', '│');\n return Array.from({ length: height }, (_, index) => {\n const row = `${fit(left[index] ?? '', leftContent)} ${divider} ${fit(right[index] ?? '', rightContent)}`;\n return truncateToWidth(row, width, ELLIPSIS);\n });\n }\n\n private renderStacked(width: number, height: number): string[] {\n const topHeight = Math.max(1, Math.floor((height - 1) / 2));\n const bottomHeight = Math.max(0, height - topHeight - 1);\n this.bodyHeight = topHeight;\n const top = this.treeLines(width);\n const bottom = this.detailLines(width);\n return [\n ...top.slice(0, topHeight),\n ...Array.from({ length: Math.max(0, topHeight - top.length) }, () => ''),\n this.theme.fg('borderMuted', '─'.repeat(width)),\n ...bottom.slice(0, bottomHeight),\n ];\n }\n}\n\nexport async function openToolsOverlay(ctx: ExtensionContext, sources: readonly ToolSource[]): Promise<void> {\n await ctx.ui.custom<undefined>(\n (tui, theme, _keybindings, done) => new ToolsOverlayComponent(tui, theme, sources, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { DoomOverlay, DoomOverlayChrome, DoomOverlayTui } from "./doomOverlay.cjs";
|
|
2
|
+
import { ToolSource } from "../toolInventory.cjs";
|
|
3
|
+
import { ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
//#region src/components/toolsOverlay.d.ts
|
|
5
|
+
declare class ToolsOverlayComponent extends DoomOverlay {
|
|
6
|
+
private readonly sources;
|
|
7
|
+
private readonly done;
|
|
8
|
+
private selected;
|
|
9
|
+
private bodyHeight;
|
|
10
|
+
constructor(tui: DoomOverlayTui, theme: Theme, sources: readonly ToolSource[], done: (result: undefined) => void);
|
|
11
|
+
/** Every source is always expanded: the source row is a heading, not a control. */
|
|
12
|
+
private rows;
|
|
13
|
+
private currentTool;
|
|
14
|
+
/** Source rows are labels, so the cursor steps over them to the next tool. */
|
|
15
|
+
private move;
|
|
16
|
+
handleInput(data: string): void;
|
|
17
|
+
private treeLines;
|
|
18
|
+
private field;
|
|
19
|
+
private toolLines;
|
|
20
|
+
private detailLines;
|
|
21
|
+
protected getChrome(): DoomOverlayChrome;
|
|
22
|
+
private hintLine;
|
|
23
|
+
protected renderBody(width: number, height: number): string[];
|
|
24
|
+
private renderStacked;
|
|
25
|
+
}
|
|
26
|
+
declare function openToolsOverlay(ctx: ExtensionContext, sources: readonly ToolSource[]): Promise<void>;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { ToolsOverlayComponent, openToolsOverlay };
|
|
29
|
+
//# sourceMappingURL=toolsOverlay.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolsOverlay.d.cts","names":[],"sources":["../../src/components/toolsOverlay.ts"],"mappings":";;;;cAmGa,8BAA8B;mBACxB;mBACA;UACT;UACA;EAEI,YAAA,KAAK,gBAAgB,OAAO,OAAO,kBAAkB,cAAc,OAAO;;UAQ9E;UASA;;UAMA;EAeR,YAAY;UAWJ;UA2BA;UAOA;UAoCA;YAME,aAAa;UAcf;YAOE,WAAW,eAAe;UAkB5B;;iBAeY,iBAAiB,KAAK,kBAAkB,kBAAkB,eAAe"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { DoomOverlay, DoomOverlayChrome, DoomOverlayTui } from "./doomOverlay.mjs";
|
|
2
|
+
import { ToolSource } from "../toolInventory.mjs";
|
|
3
|
+
import { ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
//#region src/components/toolsOverlay.d.ts
|
|
5
|
+
declare class ToolsOverlayComponent extends DoomOverlay {
|
|
6
|
+
private readonly sources;
|
|
7
|
+
private readonly done;
|
|
8
|
+
private selected;
|
|
9
|
+
private bodyHeight;
|
|
10
|
+
constructor(tui: DoomOverlayTui, theme: Theme, sources: readonly ToolSource[], done: (result: undefined) => void);
|
|
11
|
+
/** Every source is always expanded: the source row is a heading, not a control. */
|
|
12
|
+
private rows;
|
|
13
|
+
private currentTool;
|
|
14
|
+
/** Source rows are labels, so the cursor steps over them to the next tool. */
|
|
15
|
+
private move;
|
|
16
|
+
handleInput(data: string): void;
|
|
17
|
+
private treeLines;
|
|
18
|
+
private field;
|
|
19
|
+
private toolLines;
|
|
20
|
+
private detailLines;
|
|
21
|
+
protected getChrome(): DoomOverlayChrome;
|
|
22
|
+
private hintLine;
|
|
23
|
+
protected renderBody(width: number, height: number): string[];
|
|
24
|
+
private renderStacked;
|
|
25
|
+
}
|
|
26
|
+
declare function openToolsOverlay(ctx: ExtensionContext, sources: readonly ToolSource[]): Promise<void>;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { ToolsOverlayComponent, openToolsOverlay };
|
|
29
|
+
//# sourceMappingURL=toolsOverlay.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolsOverlay.d.mts","names":[],"sources":["../../src/components/toolsOverlay.ts"],"mappings":";;;;cAmGa,8BAA8B;mBACxB;mBACA;UACT;UACA;EAEI,YAAA,KAAK,gBAAgB,OAAO,OAAO,kBAAkB,cAAc,OAAO;;UAQ9E;UASA;;UAMA;EAeR,YAAY;UAWJ;UA2BA;UAOA;UAoCA;YAME,aAAa;UAcf;YAOE,WAAW,eAAe;UAkB5B;;iBAeY,iBAAiB,KAAK,kBAAkB,kBAAkB,eAAe"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{DOOM_FULLSCREEN_UI_OPTIONS as e,DOOM_OVERLAY_ACCENT as t,DoomOverlay as n}from"./doomOverlay.mjs";import{matchesKey as r,truncateToWidth as i,visibleWidth as a}from"@earendil-works/pi-tui";const o=[[`↑↓`,`move`],[`esc`,`close`]],s=`No tools are registered in this session.`;function c(e,t){let n=i(e,Math.max(0,t),`…`);return n+` `.repeat(Math.max(0,t-a(n)))}function l(e,t,n){let r=a(t),i=Math.max(0,n-r-1);return c(e,i)+` `.repeat(Math.max(1,n-i-r))+c(t,r)}function u(e,t){if(t<=0)return[];let n=[],r=``;for(let i of e.split(/\s+/).filter(Boolean))r.length===0?r=i:r.length+1+i.length<=t?r+=` ${i}`:(n.push(r),r=i);return r.length>0&&n.push(r),n}function d(e){if(typeof e!=`object`||!e)return[];let t=e;if(typeof t.properties!=`object`||t.properties===null)return[];let n=new Set(Array.isArray(t.required)?t.required.filter(e=>typeof e==`string`):[]);return Object.entries(t.properties).map(([e,t])=>{let r=typeof t==`object`&&t?t.type:void 0;return{name:e,type:typeof r==`string`?r:``,required:n.has(e)}})}function f(e){return e.tools.reduce((e,t)=>e+ +!!t.active,0)}var p=class extends n{sources;done;selected;bodyHeight=1;constructor(e,t,n,r){super(e,t),this.sources=n,this.done=r,this.selected=this.rows().findIndex(e=>e.kind===`tool`)}rows(){let e=[];for(let t of this.sources){e.push({kind:`source`,source:t});for(let n of t.tools)e.push({kind:`tool`,source:t,tool:n})}return e}currentTool(){let e=this.rows()[this.selected];return e?.kind===`tool`?e:void 0}move(e){let t=this.rows(),n=e<0?-1:1,r=this.selected;for(let i=Math.abs(e);i>0;--i){let e=r+n;for(;e>=0&&e<t.length&&t[e]?.kind!==`tool`;)e+=n;if(e<0||e>=t.length)break;r=e}r!==this.selected&&(this.selected=r,this.tui.requestRender())}handleInput(e){if(r(e,`escape`)||r(e,`ctrl+c`)||e===`q`){this.done(void 0);return}if(r(e,`up`)||e===`k`)return this.move(-1);if(r(e,`down`)||e===`j`)return this.move(1);if(r(e,`pageUp`))return this.move(-Math.max(1,this.bodyHeight-1));if(r(e,`pageDown`))return this.move(Math.max(1,this.bodyHeight-1))}treeLines(e){let t=this.rows(),n=[this.theme.bold(`SOURCES`),``];if(t.length===0)return n.push(this.theme.fg(`dim`,s)),n;let r=Math.max(1,this.bodyHeight-n.length),i=Math.max(0,Math.min(this.selected,t.length-1)),a=Math.max(0,Math.min(i-r+1,Math.max(0,t.length-r)));for(let[o,s]of t.slice(a,a+r).entries()){let t=s.kind===`source`?l(this.theme.bold(s.source.label),this.theme.fg(`dim`,s.source.status??String(f(s.source))),e):this.theme.fg(s.tool.active?`text`:`dim`,c(` ${s.tool.name}`,e));n.push(a+o===i?this.theme.bg(`selectedBg`,c(t,e)):t)}return n}field(e,t,n){let r=Math.max(1,n-12);return u(t,r).map((t,n)=>`${this.theme.fg(`dim`,c(n===0?e:``,12))}${c(t,r)}`)}toolLines(e,t,n){let r=[this.theme.bold(t.name),``];r.push(...this.field(`source`,e.label,n)),e.detail&&r.push(...this.field(`registered`,e.detail,n)),r.push(...this.field(`status`,t.active?this.theme.fg(`success`,`active`):this.theme.fg(`dim`,`inactive`),n)),t.description&&(r.push(``),r.push(...u(t.description,n)));let i=d(t.parameters);if(i.length>0){r.push(``),r.push(this.theme.fg(`dim`,`PARAMETERS`));for(let e of i){let t=e.required?`${e.name}${this.theme.fg(`warning`,` *`)}`:e.name;r.push(l(t,this.theme.fg(`dim`,e.type),n))}}for(let e of t.promptGuidelines??[])r.push(``),r.push(...u(e,n));return r}detailLines(e){let t=this.currentTool();return t?this.toolLines(t.source,t.tool,e):[this.theme.fg(`dim`,s)]}getChrome(){let e=`${this.sources.reduce((e,t)=>e+f(t),0)}/${this.sources.reduce((e,t)=>e+t.tools.length,0)}`;return{title:`TOOLS`,accent:t,breadcrumb:`SPC › e / t · tools`,headerRight:`${e} tools · ${this.sources.length} sources · esc close`,footer:this.hintLine()}}hintLine(){return o.map(([e,t])=>`${this.theme.bg(`selectedBg`,this.theme.fg(`text`,` ${e} `))}${this.theme.fg(`dim`,` ${t}`)}`).join(this.theme.fg(`dim`,` `))}renderBody(e,t){if(this.bodyHeight=t,e<48)return this.renderStacked(e,t);let n=Math.max(18,Math.floor((e-1)*.3333333333333333)),r=Math.max(1,e-n-1),a=Math.max(1,n-1),o=Math.max(1,r-1),s=this.treeLines(a),l=this.detailLines(o),u=this.theme.fg(`borderMuted`,`│`);return Array.from({length:t},(t,n)=>{let r=`${c(s[n]??``,a)} ${u} ${c(l[n]??``,o)}`;return i(r,e,`…`)})}renderStacked(e,t){let n=Math.max(1,Math.floor((t-1)/2)),r=Math.max(0,t-n-1);this.bodyHeight=n;let i=this.treeLines(e),a=this.detailLines(e);return[...i.slice(0,n),...Array.from({length:Math.max(0,n-i.length)},()=>``),this.theme.fg(`borderMuted`,`─`.repeat(e)),...a.slice(0,r)]}};async function m(t,n){await t.ui.custom((e,t,r,i)=>new p(e,t,n,i),e)}export{p as ToolsOverlayComponent,m as openToolsOverlay};
|
|
2
|
+
//# sourceMappingURL=toolsOverlay.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolsOverlay.mjs","names":[],"sources":["../../src/components/toolsOverlay.ts"],"sourcesContent":["/**\n * Tools browser: the `SPC e t` overlay.\n *\n * A grouped list on the left (Pi core, one group per MCP server, one per\n * extension) and a detail pane on the right for the tool under the cursor.\n * Read-only: nothing here enables or disables a tool, so it cannot fight plan\n * mode or the workflow dispatcher over the active tool set.\n *\n * Group headings are labels, not controls: everything is always expanded and\n * the cursor steps over them, so there is no expansion state to track and the\n * whole inventory is legible without navigating it. The tree stays a flat row\n * list, which keeps selection a single index and every row assertable as text.\n */\n\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\nimport type { ToolEntry, ToolSource } from '../toolInventory.ts';\nimport {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from './doomOverlay.ts';\n\ntype Row = { kind: 'source'; source: ToolSource } | { kind: 'tool'; source: ToolSource; tool: ToolEntry };\n\nconst TITLE = 'TOOLS';\nconst BREADCRUMB = 'SPC › e / t · tools';\nconst HEADER_RIGHT = 'esc close';\nconst HINTS: readonly (readonly [string, string])[] = [\n ['↑↓', 'move'],\n ['esc', 'close'],\n];\n\nconst MIN_WIDTH = 48;\n/** A third for the tree, two thirds for the tool being read. */\nconst TREE_PANE_RATIO = 1 / 3;\n/** One blank column each side of the pane divider so neither column touches it. */\nconst PANE_GUTTER = 1;\nconst MIN_PANE_WIDTH = 18;\nconst ELLIPSIS = '…';\nconst TOOL_INDENT = ' ';\nconst LABEL_COLUMN = 12;\nconst REQUIRED_MARKER = ' *';\nconst EMPTY_MESSAGE = 'No tools are registered in this session.';\nconst ACTIVE_LABEL = 'active';\nconst INACTIVE_LABEL = 'inactive';\nconst SOURCE_HEADING = 'SOURCES';\n\nfunction fit(text: string, width: number): string {\n const clipped = truncateToWidth(text, Math.max(0, width), ELLIPSIS);\n return clipped + ' '.repeat(Math.max(0, width - visibleWidth(clipped)));\n}\n\nfunction rightAligned(left: string, right: string, width: number): string {\n const rightWidth = visibleWidth(right);\n const leftWidth = Math.max(0, width - rightWidth - 1);\n return fit(left, leftWidth) + ' '.repeat(Math.max(1, width - leftWidth - rightWidth)) + fit(right, rightWidth);\n}\n\nfunction wrap(text: string, width: number): string[] {\n if (width <= 0) return [];\n const lines: string[] = [];\n let current = '';\n for (const word of text.split(/\\s+/).filter(Boolean)) {\n if (current.length === 0) current = word;\n else if (current.length + 1 + word.length <= width) current += ` ${word}`;\n else {\n lines.push(current);\n current = word;\n }\n }\n if (current.length > 0) lines.push(current);\n return lines;\n}\n\n/** Top-level properties of a JSON-schema-shaped parameter object, if it has any. */\nfunction parameterRows(parameters: unknown): readonly { name: string; type: string; required: boolean }[] {\n if (typeof parameters !== 'object' || parameters === null) return [];\n const schema = parameters as { properties?: unknown; required?: unknown };\n if (typeof schema.properties !== 'object' || schema.properties === null) return [];\n const required = new Set(\n Array.isArray(schema.required) ? schema.required.filter((name) => typeof name === 'string') : [],\n );\n return Object.entries(schema.properties as Record<string, unknown>).map(([name, value]) => {\n const type = typeof value === 'object' && value !== null ? (value as { type?: unknown }).type : undefined;\n return {\n name,\n type: typeof type === 'string' ? type : '',\n required: required.has(name),\n };\n });\n}\n\nfunction activeCount(source: ToolSource): number {\n return source.tools.reduce((total, tool) => total + (tool.active ? 1 : 0), 0);\n}\n\nexport class ToolsOverlayComponent extends DoomOverlay {\n private readonly sources: readonly ToolSource[];\n private readonly done: (result: undefined) => void;\n private selected: number;\n private bodyHeight = 1;\n\n constructor(tui: DoomOverlayTui, theme: Theme, sources: readonly ToolSource[], done: (result: undefined) => void) {\n super(tui, theme);\n this.sources = sources;\n this.done = done;\n this.selected = this.rows().findIndex((row) => row.kind === 'tool');\n }\n\n /** Every source is always expanded: the source row is a heading, not a control. */\n private rows(): readonly Row[] {\n const rows: Row[] = [];\n for (const source of this.sources) {\n rows.push({ kind: 'source', source });\n for (const tool of source.tools) rows.push({ kind: 'tool', source, tool });\n }\n return rows;\n }\n\n private currentTool(): Extract<Row, { kind: 'tool' }> | undefined {\n const row = this.rows()[this.selected];\n return row?.kind === 'tool' ? row : undefined;\n }\n\n /** Source rows are labels, so the cursor steps over them to the next tool. */\n private move(delta: number): void {\n const rows = this.rows();\n const step = delta < 0 ? -1 : 1;\n let index = this.selected;\n for (let remaining = Math.abs(delta); remaining > 0; remaining -= 1) {\n let next = index + step;\n while (next >= 0 && next < rows.length && rows[next]?.kind !== 'tool') next += step;\n if (next < 0 || next >= rows.length) break;\n index = next;\n }\n if (index === this.selected) return;\n this.selected = index;\n this.tui.requestRender();\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c') || data === 'q') {\n this.done(undefined);\n return;\n }\n if (matchesKey(data, 'up') || data === 'k') return this.move(-1);\n if (matchesKey(data, 'down') || data === 'j') return this.move(1);\n if (matchesKey(data, 'pageUp')) return this.move(-Math.max(1, this.bodyHeight - 1));\n if (matchesKey(data, 'pageDown')) return this.move(Math.max(1, this.bodyHeight - 1));\n }\n\n private treeLines(width: number): string[] {\n const rows = this.rows();\n const lines = [this.theme.bold(SOURCE_HEADING), ''];\n if (rows.length === 0) {\n lines.push(this.theme.fg('dim', EMPTY_MESSAGE));\n return lines;\n }\n\n const budget = Math.max(1, this.bodyHeight - lines.length);\n const selected = Math.max(0, Math.min(this.selected, rows.length - 1));\n const start = Math.max(0, Math.min(selected - budget + 1, Math.max(0, rows.length - budget)));\n for (const [offset, row] of rows.slice(start, start + budget).entries()) {\n const line =\n row.kind === 'source'\n ? rightAligned(\n this.theme.bold(row.source.label),\n this.theme.fg('dim', row.source.status ?? String(activeCount(row.source))),\n width,\n )\n : // Colour separates the active tool set from the merely registered one,\n // so the cursor never has to visit a row to learn its state.\n this.theme.fg(row.tool.active ? 'text' : 'dim', fit(`${TOOL_INDENT}${row.tool.name}`, width));\n lines.push(start + offset === selected ? this.theme.bg('selectedBg', fit(line, width)) : line);\n }\n return lines;\n }\n\n private field(label: string, value: string, width: number): string[] {\n const valueWidth = Math.max(1, width - LABEL_COLUMN);\n return wrap(value, valueWidth).map(\n (line, index) => `${this.theme.fg('dim', fit(index === 0 ? label : '', LABEL_COLUMN))}${fit(line, valueWidth)}`,\n );\n }\n\n private toolLines(source: ToolSource, tool: ToolEntry, width: number): string[] {\n const lines = [this.theme.bold(tool.name), ''];\n lines.push(...this.field('source', source.label, width));\n // Source rows no longer open a detail pane, so the entry path is shown here.\n if (source.detail) lines.push(...this.field('registered', source.detail, width));\n lines.push(\n ...this.field(\n 'status',\n tool.active ? this.theme.fg('success', ACTIVE_LABEL) : this.theme.fg('dim', INACTIVE_LABEL),\n width,\n ),\n );\n if (tool.description) {\n lines.push('');\n lines.push(...wrap(tool.description, width));\n }\n const parameters = parameterRows(tool.parameters);\n if (parameters.length > 0) {\n lines.push('');\n lines.push(this.theme.fg('dim', 'PARAMETERS'));\n for (const parameter of parameters) {\n // The name takes the whole line so long ones stop truncating into the\n // type, which sits flush right where the eye can run down the column.\n const name = parameter.required\n ? `${parameter.name}${this.theme.fg('warning', REQUIRED_MARKER)}`\n : parameter.name;\n lines.push(rightAligned(name, this.theme.fg('dim', parameter.type), width));\n }\n }\n for (const guideline of tool.promptGuidelines ?? []) {\n lines.push('');\n lines.push(...wrap(guideline, width));\n }\n return lines;\n }\n\n private detailLines(width: number): string[] {\n const row = this.currentTool();\n if (!row) return [this.theme.fg('dim', EMPTY_MESSAGE)];\n return this.toolLines(row.source, row.tool, width);\n }\n\n protected getChrome(): DoomOverlayChrome {\n // Active over registered: the first number is what the model can call, the\n // second is what a different mode or profile could turn back on.\n const active = this.sources.reduce((total, source) => total + activeCount(source), 0);\n const tools = `${active}/${this.sources.reduce((total, source) => total + source.tools.length, 0)}`;\n return {\n title: TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: BREADCRUMB,\n headerRight: `${tools} tools · ${this.sources.length} sources · ${HEADER_RIGHT}`,\n footer: this.hintLine(),\n };\n }\n\n private hintLine(): string {\n return HINTS.map(\n ([key, label]) =>\n `${this.theme.bg('selectedBg', this.theme.fg('text', ` ${key} `))}${this.theme.fg('dim', ` ${label}`)}`,\n ).join(this.theme.fg('dim', ' '));\n }\n\n protected renderBody(width: number, height: number): string[] {\n this.bodyHeight = height;\n if (width < MIN_WIDTH) return this.renderStacked(width, height);\n\n const leftWidth = Math.max(MIN_PANE_WIDTH, Math.floor((width - 1) * TREE_PANE_RATIO));\n const rightWidth = Math.max(1, width - leftWidth - 1);\n const leftContent = Math.max(1, leftWidth - PANE_GUTTER);\n const rightContent = Math.max(1, rightWidth - PANE_GUTTER);\n const left = this.treeLines(leftContent);\n const right = this.detailLines(rightContent);\n\n const divider = this.theme.fg('borderMuted', '│');\n return Array.from({ length: height }, (_, index) => {\n const row = `${fit(left[index] ?? '', leftContent)} ${divider} ${fit(right[index] ?? '', rightContent)}`;\n return truncateToWidth(row, width, ELLIPSIS);\n });\n }\n\n private renderStacked(width: number, height: number): string[] {\n const topHeight = Math.max(1, Math.floor((height - 1) / 2));\n const bottomHeight = Math.max(0, height - topHeight - 1);\n this.bodyHeight = topHeight;\n const top = this.treeLines(width);\n const bottom = this.detailLines(width);\n return [\n ...top.slice(0, topHeight),\n ...Array.from({ length: Math.max(0, topHeight - top.length) }, () => ''),\n this.theme.fg('borderMuted', '─'.repeat(width)),\n ...bottom.slice(0, bottomHeight),\n ];\n }\n}\n\nexport async function openToolsOverlay(ctx: ExtensionContext, sources: readonly ToolSource[]): Promise<void> {\n await ctx.ui.custom<undefined>(\n (tui, theme, _keybindings, done) => new ToolsOverlayComponent(tui, theme, sources, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"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"}
|
package/dist/config.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./_virtual/_rolldown/runtime.cjs");var t=e.__exportAll({});e.__reExport(t,require("@agimon-ai/doompi-extension-contracts/config")),Object.defineProperty(exports,"config_exports",{enumerable:!0,get:function(){return t}});var n=require("@agimon-ai/doompi-extension-contracts/config");Object.keys(n).forEach(function(e){e!=="default"&&!Object.prototype.hasOwnProperty.call(exports,e)&&Object.defineProperty(exports,e,{enumerable:!0,get:function(){return n[e]}})});
|
|
2
|
+
//# sourceMappingURL=config.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.cjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["export * from '@agimon-ai/doompi-extension-contracts/config';\n"],"mappings":""}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export * from "@agimon-ai/doompi-extension-contracts/config";
|
|
2
|
+
import * as import__agimon_ai_doompi_extension_contracts_config from "@agimon-ai/doompi-extension-contracts/config";
|
|
3
|
+
//#endregion
|
|
4
|
+
export { import__agimon_ai_doompi_extension_contracts_config as config_d_exports };
|
|
5
|
+
//# sourceMappingURL=config.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.cts","names":[],"sources":["../src/config.ts"],"mappings":""}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export * from "@agimon-ai/doompi-extension-contracts/config";
|
|
2
|
+
import * as import__agimon_ai_doompi_extension_contracts_config from "@agimon-ai/doompi-extension-contracts/config";
|
|
3
|
+
//#endregion
|
|
4
|
+
export { import__agimon_ai_doompi_extension_contracts_config as config_d_exports };
|
|
5
|
+
//# sourceMappingURL=config.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.mts","names":[],"sources":["../src/config.ts"],"mappings":""}
|
package/dist/config.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{__exportAll as e,__reExport as t}from"./_virtual/_rolldown/runtime.mjs";export*from"@agimon-ai/doompi-extension-contracts/config";var n=e({});import*as r from"@agimon-ai/doompi-extension-contracts/config";t(n,r);export{n as config_exports};
|
|
2
|
+
//# sourceMappingURL=config.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.mjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["export * from '@agimon-ai/doompi-extension-contracts/config';\n"],"mappings":""}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./_virtual/_rolldown/runtime.cjs"),t=require("./leaderRegistry.cjs"),n=require("./components/doomEditor.cjs"),r=require("./components/doomFooter.cjs"),i=require("./components/doomHeader.cjs"),a=require("./components/leaderHints.cjs"),o=require("./config.cjs"),s=require("./footer.cjs"),c=require("./logSinkTelemetry.cjs");require("./theme.cjs");const l=require("./uiState.cjs"),u=require("./components/configOverlay.cjs"),d=require("./components/toolsOverlay.cjs"),f=require("./mode.cjs"),p=require("./toolInventory.cjs");let m=require("node:path");m=e.__toESM(m,1);let h=require("@agimon-ai/doompi-extension-contracts/leader"),g=require("@agimon-ai/doompi-extension-contracts/mcp-status"),_=require("@agimon-ai/doompi-extension-contracts/protocol");const v=`doom-pi-leader`,y=`belowEditor`,b=`warning`,x=`doom-pi`,S=new Set([`hotkeys`]);function C(e,t){let n=e.trim().replace(/\s+/g,` `);if(!n)return;let r=n.slice(0,64).trimEnd();return`${x} · ${r} · ${m.default.basename(t)||t}`}function w(e,w=c.createUiTelemetry()){let T=new l.DoomUiState,E=new s.footer_exports.DoomFooterStatusRegistry,D=new f.mode_exports.DoomModeStatusRegistry,O=new o.config_exports.DoomConfigRegistry,k=new t.DoomLeaderRegistry,A=new Set,j=new Map,M,N,P,F,I,L,R,z,B=new Map,V=0,H=0,U=!1,W,G=async()=>{if(W)try{return(await W.request(g.McpStatusQueryProtocol,{},{timeoutMs:250})).servers}catch{return}};e.registerCommand(`tools`,{description:`Browse the tools available in this session`,handler:async(t,n)=>{let r=await G(),i=p.buildToolSources({tools:e.getAllTools(),activeTools:e.getActiveTools(),...r?{mcpServers:r}:{}});await d.openToolsOverlay(n,i)}}),e.registerCommand(`config`,{description:`Browse and change the settings extensions contribute`,handler:async(e,t)=>{await u.openConfigOverlay(t,O)}});let K=e=>{for(let t of e){let e=t.bindingId?` (${t.bindingId})`:``,n=`Leader contribution from ${t.source}${e}: ${t.message}`;A.has(n)||(A.add(n),w.recordWarning(c.UI_EVENT.leaderContributionRejected,t.message,{"leader.source":t.source,...t.bindingId?{"leader.binding.id":t.bindingId}:{}}),N?.mode===`tui`&&N.ui.notify(n,b))}},q=Object.freeze({register(e){let t=Symbol(e.source),n=k.register(e);if(K(n.diagnostics),!n.accepted)return()=>void 0;j.set(e.source,t);let r=!1;return()=>{r||(r=!0,j.get(e.source)===t&&(j.delete(e.source),k.register({source:e.source,bindings:[]})))}}});e.on(`session_start`,(t,o)=>{N=o;let s=++H,l=(0,_.createProtocolRuntime)({bus:e.events,source:`@agimon-ai/doompi-ui`,sessionId:o.sessionManager.getSessionId()});if(W=l,P?.(),E.reset(),P=E.bind(l),F?.(),D.reset(),F=D.bind(l),L?.(),O.reset(),L=O.bind(l),R?.(),z?.(),R=l.provide(h.LeaderRegistrationProtocol,({contribution:e,generation:t},n)=>{if(n.source!==e.source)throw Error(`Leader contribution source does not match the registration envelope source.`);let r=`${n.source}:${t}:${++V}`,i={source:e.source,bindings:e.bindings.map(e=>{let[t,...n]=e.path;if(!t)throw Error(`Leader binding '${e.id}' has no path.`);return{...e,path:[t,...n]}})};return B.set(r,q.register(i)),{handleId:r}}),z=l.provide(h.LeaderUnregistrationProtocol,({handleId:e})=>{let t=B.get(e);return t?(B.delete(e),t(),{removed:!0}):{removed:!1}}),U=o.sessionManager.getEntries().some(e=>e.type===`message`),o.mode!==`tui`)return;for(let e of A)o.ui.notify(e,b);let u=process.env.DOOMPI_THEME||`doom-pi-dark`,d=o.ui.setTheme(u);d.success||(o.ui.notify(`Could not apply Doom Pi theme: ${d.error}`,b),w.recordWarning(c.UI_EVENT.themeApplyFailed,d.error??`Unknown theme error`,{"ui.theme":u})),I?.(),I=D.subscribe(()=>T.setModes(D.getStatuses())),T.setModes(D.getStatuses()),o.ui.setTitle(`${x} · ${m.default.basename(o.cwd)||o.cwd} · pi/coding`),o.ui.setHeader((e,t)=>new i.DoomHeader(t,o.cwd,void 0,o,()=>e.terminal.rows)),o.ui.setFooter((e,t,n)=>(M=n,new r.DoomFooter(e,t,o,n,E,T))),o.ui.setEditorComponent((t,r,i)=>new n.DoomEditor(t,r,i,T,e=>{if(!e.active){o.ui.setWidget(v,void 0,{placement:y});return}o.ui.setWidget(v,(t,n)=>new a.LeaderHints(n,e,o,M,T.getModes()),{placement:y})},void 0,o.ui.theme,{registry:k,isCommandAvailable:t=>S.has(t)||e.getCommands().some(e=>e.name===t),onLeaderAction:(e,t)=>{if(N!==o||H!==s){o.ui.notify(`Leader action ${t} is unavailable.`,b);return}l.notify(h.LeaderActionInvoked,{source:e,action:t})},onUnavailableCommand:e=>{w.recordWarning(c.UI_EVENT.leaderCommandUnavailable,`Leader command /${e} is unavailable.`,{"ui.kind":`command`}),o.ui.notify(`Leader command /${e} is unavailable.`,b)},onUnavailableAction:e=>{w.recordWarning(c.UI_EVENT.leaderActionUnavailable,`Leader action ${e} is unavailable.`,{"ui.kind":`action`}),o.ui.notify(`Leader action ${e} is unavailable.`,b)}})),w.recordEvent(c.UI_EVENT.shellInstalled,{"ui.theme":u,"ui.theme.applied":d.success,"ui.leader.group.count":k.getGroup([])?.options.length??0})}),e.on(`input`,(e,t)=>{if(t.mode!==`tui`||e.source!==`interactive`||U)return;U=!0;let n=C(e.text,t.cwd);n&&t.ui.setTitle(n)}),e.on(`session_shutdown`,async(e,t)=>{++H;for(let e of B.values())e();B.clear(),N=void 0,M=void 0,P?.(),P=void 0,E.reset(),F?.(),F=void 0,I?.(),I=void 0,D.reset(),L?.(),L=void 0,R?.(),R=void 0,z?.(),z=void 0,O.reset(),T.reset(),W=void 0,t.mode===`tui`&&t.ui.setWidget(v,void 0,{placement:y}),await w.shutdown()})}exports.doomPiUiExtension=w;
|
|
2
|
+
//# sourceMappingURL=extension.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.cjs","names":["path","createUiTelemetry","DoomUiState","DoomLeaderRegistry","McpStatusQueryProtocol","buildToolSources","openToolsOverlay","openConfigOverlay","UI_EVENT","createProtocolRuntime","LeaderRegistrationProtocol","LeaderUnregistrationProtocol","DoomHeader","DoomFooter","DoomEditor","LeaderHints","LeaderActionInvoked"],"sources":["../src/extension.ts"],"sourcesContent":["import path from 'node:path';\nimport type { ExtensionAPI, ExtensionContext, ReadonlyFooterDataProvider } from '@earendil-works/pi-coding-agent';\nimport {\n LeaderActionInvoked,\n type LeaderContribution,\n LeaderRegistrationProtocol,\n LeaderUnregistrationProtocol,\n} from '@agimon-ai/doompi-extension-contracts/leader';\nimport { McpStatusQueryProtocol } from '@agimon-ai/doompi-extension-contracts/mcp-status';\nimport { createProtocolRuntime, type ProtocolRuntime } from '@agimon-ai/doompi-extension-contracts/protocol';\nimport { DoomEditor } from './components/doomEditor.ts';\nimport { DoomFooter } from './components/doomFooter.ts';\nimport { DoomHeader } from './components/doomHeader.ts';\nimport { LeaderHints } from './components/leaderHints.ts';\nimport { DoomConfigRegistry } from './config.ts';\nimport { DoomFooterStatusRegistry } from './footer.ts';\nimport { DoomModeStatusRegistry } from './mode.ts';\nimport { DoomLeaderRegistry, type DoomLeaderDiagnostic } from './leaderRegistry.ts';\nimport { openConfigOverlay } from './components/configOverlay.ts';\nimport { openToolsOverlay } from './components/toolsOverlay.ts';\nimport { buildToolSources, type McpServerStatus } from './toolInventory.ts';\nimport { createUiTelemetry, UI_EVENT, type UiTelemetry } from './logSinkTelemetry.ts';\nimport { DEFAULT_THEME_NAME } from './theme.ts';\nimport { DoomUiState, type LeaderSnapshot } from './uiState.ts';\n\nconst LEADER_WIDGET_KEY = 'doom-pi-leader';\nconst TUI_MODE = 'tui';\nconst WIDGET_PLACEMENT = 'belowEditor';\nconst WARNING_STYLE = 'warning';\nconst THEME_ENVIRONMENT_KEY = 'DOOMPI_THEME';\nconst HOTKEYS_COMMAND = 'hotkeys';\nconst TOOLS_COMMAND = 'tools';\nconst CONFIG_COMMAND = 'config';\n/** Short: `/tools` must open promptly whether or not an MCP extension answers. */\nconst MCP_STATUS_TIMEOUT_MS = 250;\nconst SESSION_START_EVENT = 'session_start';\nconst SESSION_MESSAGE_ENTRY = 'message';\nconst INPUT_EVENT = 'input';\nconst INTERACTIVE_INPUT_SOURCE = 'interactive';\nconst SESSION_SHUTDOWN_EVENT = 'session_shutdown';\nconst TITLE_MESSAGE_MAX_LENGTH = 64;\nconst TITLE_PREFIX = 'doom-pi';\nconst BUILTIN_LEADER_COMMANDS = new Set([HOTKEYS_COMMAND]);\n\nfunction createMessageTitle(message: string, cwd: string): string | undefined {\n const normalizedMessage = message.trim().replace(/\\s+/g, ' ');\n if (!normalizedMessage) return undefined;\n const messageTitle = normalizedMessage.slice(0, TITLE_MESSAGE_MAX_LENGTH).trimEnd();\n return `${TITLE_PREFIX} · ${messageTitle} · ${path.basename(cwd) || cwd}`;\n}\n\nexport function doomPiUiExtension(pi: ExtensionAPI, telemetry: UiTelemetry = createUiTelemetry()): void {\n const uiState = new DoomUiState();\n const footerStatusRegistry = new DoomFooterStatusRegistry();\n const modeStatusRegistry = new DoomModeStatusRegistry();\n const configRegistry = new DoomConfigRegistry();\n const leaderRegistry = new DoomLeaderRegistry();\n const pendingDiagnostics = new Set<string>();\n const contributionGenerations = new Map<string, symbol>();\n let footerData: ReadonlyFooterDataProvider | undefined;\n let activeContext: ExtensionContext | undefined;\n let disposeFooterStatuses: (() => void) | undefined;\n let disposeModeStatuses: (() => void) | undefined;\n let disposeModeSubscription: (() => void) | undefined;\n let disposeConfigContributions: (() => void) | undefined;\n let disposeLeaderRegistration: (() => void) | undefined;\n let disposeLeaderUnregistration: (() => void) | undefined;\n const protocolContributions = new Map<string, () => void>();\n let nextProtocolHandle = 0;\n let sessionGeneration = 0;\n let hasSessionMessage = false;\n let sessionProtocol: ProtocolRuntime | undefined;\n\n /**\n * Asks the MCP extension what it is currently connected to.\n *\n * No answer is a normal session: `--no-mcp` leaves nobody providing the query,\n * and the panel simply lists MCP tools under the extension that registered them.\n */\n const readMcpServers = async (): Promise<readonly McpServerStatus[] | undefined> => {\n if (!sessionProtocol) return undefined;\n try {\n const snapshot = await sessionProtocol.request(McpStatusQueryProtocol, {}, { timeoutMs: MCP_STATUS_TIMEOUT_MS });\n return snapshot.servers;\n } catch {\n return undefined;\n }\n };\n\n pi.registerCommand(TOOLS_COMMAND, {\n description: 'Browse the tools available in this session',\n handler: async (_args, ctx) => {\n // Read at open time so a reconnected server or a plan-mode tool swap shows\n // up without restarting the session.\n const mcpServers = await readMcpServers();\n const sources = buildToolSources({\n tools: pi.getAllTools(),\n activeTools: pi.getActiveTools(),\n ...(mcpServers ? { mcpServers } : {}),\n });\n await openToolsOverlay(ctx, sources);\n },\n });\n\n pi.registerCommand(CONFIG_COMMAND, {\n description: 'Browse and change the settings extensions contribute',\n handler: async (_args, ctx) => {\n // The registry is passed live rather than snapshotted: a contributor can be\n // fetching or installing something while the panel is open, and that\n // progress is meant to land in it.\n await openConfigOverlay(ctx, configRegistry);\n },\n });\n\n const reportDiagnostics = (diagnostics: readonly DoomLeaderDiagnostic[]): void => {\n for (const diagnostic of diagnostics) {\n const binding = diagnostic.bindingId ? ` (${diagnostic.bindingId})` : '';\n const message = `Leader contribution from ${diagnostic.source}${binding}: ${diagnostic.message}`;\n if (pendingDiagnostics.has(message)) continue;\n pendingDiagnostics.add(message);\n // A rejected contribution silently drops another extension's keybindings,\n // and the notice below only reaches a run that has a TUI attached.\n void telemetry.recordWarning(UI_EVENT.leaderContributionRejected, diagnostic.message, {\n 'leader.source': diagnostic.source,\n ...(diagnostic.bindingId ? { 'leader.binding.id': diagnostic.bindingId } : {}),\n });\n if (activeContext?.mode === TUI_MODE) activeContext.ui.notify(message, WARNING_STYLE);\n }\n };\n\n const leaderContext = Object.freeze({\n register(contribution: LeaderContribution) {\n const generation = Symbol(contribution.source);\n const result = leaderRegistry.register(contribution);\n reportDiagnostics(result.diagnostics);\n if (!result.accepted) return () => undefined;\n contributionGenerations.set(contribution.source, generation);\n let disposed = false;\n return () => {\n if (disposed) return;\n disposed = true;\n if (contributionGenerations.get(contribution.source) !== generation) return;\n contributionGenerations.delete(contribution.source);\n leaderRegistry.register({ source: contribution.source, bindings: [] });\n };\n },\n });\n pi.on(SESSION_START_EVENT, (_event, ctx) => {\n activeContext = ctx;\n const activeGeneration = ++sessionGeneration;\n const protocol = createProtocolRuntime({\n bus: pi.events,\n source: '@agimon-ai/doompi-ui',\n sessionId: ctx.sessionManager.getSessionId(),\n });\n sessionProtocol = protocol;\n disposeFooterStatuses?.();\n footerStatusRegistry.reset();\n disposeFooterStatuses = footerStatusRegistry.bind(protocol);\n disposeModeStatuses?.();\n modeStatusRegistry.reset();\n disposeModeStatuses = modeStatusRegistry.bind(protocol);\n // Bound above the TUI-mode guard below, like footer and mode: a headless run\n // still accepts contributions, it just never draws them.\n disposeConfigContributions?.();\n configRegistry.reset();\n disposeConfigContributions = configRegistry.bind(protocol);\n disposeLeaderRegistration?.();\n disposeLeaderUnregistration?.();\n disposeLeaderRegistration = protocol.provide(\n LeaderRegistrationProtocol,\n ({ contribution, generation }, identity) => {\n if (identity.source !== contribution.source) {\n throw new Error('Leader contribution source does not match the registration envelope source.');\n }\n const handleId = `${identity.source}:${generation}:${++nextProtocolHandle}`;\n const typedContribution: LeaderContribution = {\n source: contribution.source,\n bindings: contribution.bindings.map((binding) => {\n const [first, ...rest] = binding.path;\n if (!first) throw new Error(`Leader binding '${binding.id}' has no path.`);\n return { ...binding, path: [first, ...rest] };\n }),\n };\n protocolContributions.set(handleId, leaderContext.register(typedContribution));\n return { handleId };\n },\n );\n disposeLeaderUnregistration = protocol.provide(LeaderUnregistrationProtocol, ({ handleId }) => {\n const dispose = protocolContributions.get(handleId);\n if (!dispose) return { removed: false };\n protocolContributions.delete(handleId);\n dispose();\n return { removed: true };\n });\n hasSessionMessage = ctx.sessionManager.getEntries().some((entry) => entry.type === SESSION_MESSAGE_ENTRY);\n if (ctx.mode !== TUI_MODE) return;\n for (const message of pendingDiagnostics) ctx.ui.notify(message, WARNING_STYLE);\n\n const themeName = process.env[THEME_ENVIRONMENT_KEY] || DEFAULT_THEME_NAME;\n const themeResult = ctx.ui.setTheme(themeName);\n if (!themeResult.success) {\n ctx.ui.notify(`Could not apply Doom Pi theme: ${themeResult.error}`, WARNING_STYLE);\n void telemetry.recordWarning(UI_EVENT.themeApplyFailed, themeResult.error ?? 'Unknown theme error', {\n 'ui.theme': themeName,\n });\n }\n\n // The modes go into shared state rather than a widget of their own: the\n // editor prints their names beside its badge and the leader panel prints\n // them with their detail, so there is no third surface to paint.\n disposeModeSubscription?.();\n disposeModeSubscription = modeStatusRegistry.subscribe(() => uiState.setModes(modeStatusRegistry.getStatuses()));\n uiState.setModes(modeStatusRegistry.getStatuses());\n\n ctx.ui.setTitle(`${TITLE_PREFIX} · ${path.basename(ctx.cwd) || ctx.cwd} · pi/coding`);\n ctx.ui.setHeader((tui, theme) => new DoomHeader(theme, ctx.cwd, undefined, ctx, () => tui.terminal.rows));\n ctx.ui.setFooter((tui, theme, data) => {\n footerData = data;\n return new DoomFooter(tui, theme, ctx, data, footerStatusRegistry, uiState);\n });\n ctx.ui.setEditorComponent(\n (tui, editorTheme, keybindings) =>\n new DoomEditor(\n tui,\n editorTheme,\n keybindings,\n uiState,\n (snapshot: LeaderSnapshot) => {\n if (!snapshot.active) {\n ctx.ui.setWidget(LEADER_WIDGET_KEY, undefined, { placement: WIDGET_PLACEMENT });\n return;\n }\n ctx.ui.setWidget(\n LEADER_WIDGET_KEY,\n (_widgetTui, widgetTheme) => new LeaderHints(widgetTheme, snapshot, ctx, footerData, uiState.getModes()),\n { placement: WIDGET_PLACEMENT },\n );\n },\n undefined,\n ctx.ui.theme,\n {\n registry: leaderRegistry,\n isCommandAvailable: (commandName) =>\n BUILTIN_LEADER_COMMANDS.has(commandName) ||\n pi.getCommands().some((command) => command.name === commandName),\n onLeaderAction: (source, action) => {\n if (activeContext !== ctx || sessionGeneration !== activeGeneration) {\n ctx.ui.notify(`Leader action ${action} is unavailable.`, WARNING_STYLE);\n return;\n }\n protocol.notify(LeaderActionInvoked, { source, action });\n },\n onUnavailableCommand: (commandName) => {\n void telemetry.recordWarning(\n UI_EVENT.leaderCommandUnavailable,\n `Leader command /${commandName} is unavailable.`,\n { 'ui.kind': 'command' },\n );\n ctx.ui.notify(`Leader command /${commandName} is unavailable.`, WARNING_STYLE);\n },\n onUnavailableAction: (action) => {\n void telemetry.recordWarning(\n UI_EVENT.leaderActionUnavailable,\n `Leader action ${action} is unavailable.`,\n {\n 'ui.kind': 'action',\n },\n );\n ctx.ui.notify(`Leader action ${action} is unavailable.`, WARNING_STYLE);\n },\n },\n ),\n );\n\n void telemetry.recordEvent(UI_EVENT.shellInstalled, {\n 'ui.theme': themeName,\n 'ui.theme.applied': themeResult.success,\n 'ui.leader.group.count': leaderRegistry.getGroup([])?.options.length ?? 0,\n });\n });\n\n pi.on(INPUT_EVENT, (event, ctx) => {\n if (ctx.mode !== TUI_MODE || event.source !== INTERACTIVE_INPUT_SOURCE || hasSessionMessage) return;\n hasSessionMessage = true;\n const title = createMessageTitle(event.text, ctx.cwd);\n if (title) ctx.ui.setTitle(title);\n });\n\n pi.on(SESSION_SHUTDOWN_EVENT, async (_event, ctx) => {\n ++sessionGeneration;\n for (const dispose of protocolContributions.values()) dispose();\n protocolContributions.clear();\n activeContext = undefined;\n footerData = undefined;\n disposeFooterStatuses?.();\n disposeFooterStatuses = undefined;\n footerStatusRegistry.reset();\n disposeModeStatuses?.();\n disposeModeStatuses = undefined;\n disposeModeSubscription?.();\n disposeModeSubscription = undefined;\n modeStatusRegistry.reset();\n disposeConfigContributions?.();\n disposeConfigContributions = undefined;\n disposeLeaderRegistration?.();\n disposeLeaderRegistration = undefined;\n disposeLeaderUnregistration?.();\n disposeLeaderUnregistration = undefined;\n configRegistry.reset();\n uiState.reset();\n sessionProtocol = undefined;\n if (ctx.mode === TUI_MODE) ctx.ui.setWidget(LEADER_WIDGET_KEY, undefined, { placement: WIDGET_PLACEMENT });\n await telemetry.shutdown();\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { UiTelemetry } from "./logSinkTelemetry.cjs";
|
|
2
|
+
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
//#region src/extension.d.ts
|
|
4
|
+
declare function doomPiUiExtension(pi: ExtensionAPI, telemetry?: UiTelemetry): void;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { doomPiUiExtension };
|
|
7
|
+
//# sourceMappingURL=extension.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.d.cts","names":[],"sources":["../src/extension.ts"],"mappings":";;;iBAmDgB,kBAAkB,IAAI,cAAc,YAAW"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { UiTelemetry } from "./logSinkTelemetry.mjs";
|
|
2
|
+
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
//#region src/extension.d.ts
|
|
4
|
+
declare function doomPiUiExtension(pi: ExtensionAPI, telemetry?: UiTelemetry): void;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { doomPiUiExtension };
|
|
7
|
+
//# sourceMappingURL=extension.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.d.mts","names":[],"sources":["../src/extension.ts"],"mappings":";;;iBAmDgB,kBAAkB,IAAI,cAAc,YAAW"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{DoomLeaderRegistry as e}from"./leaderRegistry.mjs";import{DoomEditor as t}from"./components/doomEditor.mjs";import{DoomFooter as n}from"./components/doomFooter.mjs";import{DoomHeader as r}from"./components/doomHeader.mjs";import{LeaderHints as i}from"./components/leaderHints.mjs";import{config_exports as a}from"./config.mjs";import{footer_exports as o}from"./footer.mjs";import{UI_EVENT as s,createUiTelemetry as c}from"./logSinkTelemetry.mjs";import"./theme.mjs";import{DoomUiState as l}from"./uiState.mjs";import{openConfigOverlay as u}from"./components/configOverlay.mjs";import{openToolsOverlay as d}from"./components/toolsOverlay.mjs";import{mode_exports as f}from"./mode.mjs";import{buildToolSources as p}from"./toolInventory.mjs";import m from"node:path";import{LeaderActionInvoked as h,LeaderRegistrationProtocol as g,LeaderUnregistrationProtocol as _}from"@agimon-ai/doompi-extension-contracts/leader";import{McpStatusQueryProtocol as v}from"@agimon-ai/doompi-extension-contracts/mcp-status";import{createProtocolRuntime as y}from"@agimon-ai/doompi-extension-contracts/protocol";const b=`doom-pi-leader`,x=`belowEditor`,S=`warning`,C=`doom-pi`,w=new Set([`hotkeys`]);function T(e,t){let n=e.trim().replace(/\s+/g,` `);if(!n)return;let r=n.slice(0,64).trimEnd();return`${C} · ${r} · ${m.basename(t)||t}`}function E(E,D=c()){let O=new l,k=new o.DoomFooterStatusRegistry,A=new f.DoomModeStatusRegistry,j=new a.DoomConfigRegistry,M=new e,N=new Set,P=new Map,F,I,L,R,z,B,V,H,U=new Map,W=0,G=0,K=!1,q,J=async()=>{if(q)try{return(await q.request(v,{},{timeoutMs:250})).servers}catch{return}};E.registerCommand(`tools`,{description:`Browse the tools available in this session`,handler:async(e,t)=>{let n=await J(),r=p({tools:E.getAllTools(),activeTools:E.getActiveTools(),...n?{mcpServers:n}:{}});await d(t,r)}}),E.registerCommand(`config`,{description:`Browse and change the settings extensions contribute`,handler:async(e,t)=>{await u(t,j)}});let Y=e=>{for(let t of e){let e=t.bindingId?` (${t.bindingId})`:``,n=`Leader contribution from ${t.source}${e}: ${t.message}`;N.has(n)||(N.add(n),D.recordWarning(s.leaderContributionRejected,t.message,{"leader.source":t.source,...t.bindingId?{"leader.binding.id":t.bindingId}:{}}),I?.mode===`tui`&&I.ui.notify(n,S))}},X=Object.freeze({register(e){let t=Symbol(e.source),n=M.register(e);if(Y(n.diagnostics),!n.accepted)return()=>void 0;P.set(e.source,t);let r=!1;return()=>{r||(r=!0,P.get(e.source)===t&&(P.delete(e.source),M.register({source:e.source,bindings:[]})))}}});E.on(`session_start`,(e,a)=>{I=a;let o=++G,c=y({bus:E.events,source:`@agimon-ai/doompi-ui`,sessionId:a.sessionManager.getSessionId()});if(q=c,L?.(),k.reset(),L=k.bind(c),R?.(),A.reset(),R=A.bind(c),B?.(),j.reset(),B=j.bind(c),V?.(),H?.(),V=c.provide(g,({contribution:e,generation:t},n)=>{if(n.source!==e.source)throw Error(`Leader contribution source does not match the registration envelope source.`);let r=`${n.source}:${t}:${++W}`,i={source:e.source,bindings:e.bindings.map(e=>{let[t,...n]=e.path;if(!t)throw Error(`Leader binding '${e.id}' has no path.`);return{...e,path:[t,...n]}})};return U.set(r,X.register(i)),{handleId:r}}),H=c.provide(_,({handleId:e})=>{let t=U.get(e);return t?(U.delete(e),t(),{removed:!0}):{removed:!1}}),K=a.sessionManager.getEntries().some(e=>e.type===`message`),a.mode!==`tui`)return;for(let e of N)a.ui.notify(e,S);let l=process.env.DOOMPI_THEME||`doom-pi-dark`,u=a.ui.setTheme(l);u.success||(a.ui.notify(`Could not apply Doom Pi theme: ${u.error}`,S),D.recordWarning(s.themeApplyFailed,u.error??`Unknown theme error`,{"ui.theme":l})),z?.(),z=A.subscribe(()=>O.setModes(A.getStatuses())),O.setModes(A.getStatuses()),a.ui.setTitle(`${C} · ${m.basename(a.cwd)||a.cwd} · pi/coding`),a.ui.setHeader((e,t)=>new r(t,a.cwd,void 0,a,()=>e.terminal.rows)),a.ui.setFooter((e,t,r)=>(F=r,new n(e,t,a,r,k,O))),a.ui.setEditorComponent((e,n,r)=>new t(e,n,r,O,e=>{if(!e.active){a.ui.setWidget(b,void 0,{placement:x});return}a.ui.setWidget(b,(t,n)=>new i(n,e,a,F,O.getModes()),{placement:x})},void 0,a.ui.theme,{registry:M,isCommandAvailable:e=>w.has(e)||E.getCommands().some(t=>t.name===e),onLeaderAction:(e,t)=>{if(I!==a||G!==o){a.ui.notify(`Leader action ${t} is unavailable.`,S);return}c.notify(h,{source:e,action:t})},onUnavailableCommand:e=>{D.recordWarning(s.leaderCommandUnavailable,`Leader command /${e} is unavailable.`,{"ui.kind":`command`}),a.ui.notify(`Leader command /${e} is unavailable.`,S)},onUnavailableAction:e=>{D.recordWarning(s.leaderActionUnavailable,`Leader action ${e} is unavailable.`,{"ui.kind":`action`}),a.ui.notify(`Leader action ${e} is unavailable.`,S)}})),D.recordEvent(s.shellInstalled,{"ui.theme":l,"ui.theme.applied":u.success,"ui.leader.group.count":M.getGroup([])?.options.length??0})}),E.on(`input`,(e,t)=>{if(t.mode!==`tui`||e.source!==`interactive`||K)return;K=!0;let n=T(e.text,t.cwd);n&&t.ui.setTitle(n)}),E.on(`session_shutdown`,async(e,t)=>{++G;for(let e of U.values())e();U.clear(),I=void 0,F=void 0,L?.(),L=void 0,k.reset(),R?.(),R=void 0,z?.(),z=void 0,A.reset(),B?.(),B=void 0,V?.(),V=void 0,H?.(),H=void 0,j.reset(),O.reset(),q=void 0,t.mode===`tui`&&t.ui.setWidget(b,void 0,{placement:x}),await D.shutdown()})}export{E as doomPiUiExtension};
|
|
2
|
+
//# sourceMappingURL=extension.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.mjs","names":["DoomFooterStatusRegistry","DoomModeStatusRegistry","DoomConfigRegistry"],"sources":["../src/extension.ts"],"sourcesContent":["import path from 'node:path';\nimport type { ExtensionAPI, ExtensionContext, ReadonlyFooterDataProvider } from '@earendil-works/pi-coding-agent';\nimport {\n LeaderActionInvoked,\n type LeaderContribution,\n LeaderRegistrationProtocol,\n LeaderUnregistrationProtocol,\n} from '@agimon-ai/doompi-extension-contracts/leader';\nimport { McpStatusQueryProtocol } from '@agimon-ai/doompi-extension-contracts/mcp-status';\nimport { createProtocolRuntime, type ProtocolRuntime } from '@agimon-ai/doompi-extension-contracts/protocol';\nimport { DoomEditor } from './components/doomEditor.ts';\nimport { DoomFooter } from './components/doomFooter.ts';\nimport { DoomHeader } from './components/doomHeader.ts';\nimport { LeaderHints } from './components/leaderHints.ts';\nimport { DoomConfigRegistry } from './config.ts';\nimport { DoomFooterStatusRegistry } from './footer.ts';\nimport { DoomModeStatusRegistry } from './mode.ts';\nimport { DoomLeaderRegistry, type DoomLeaderDiagnostic } from './leaderRegistry.ts';\nimport { openConfigOverlay } from './components/configOverlay.ts';\nimport { openToolsOverlay } from './components/toolsOverlay.ts';\nimport { buildToolSources, type McpServerStatus } from './toolInventory.ts';\nimport { createUiTelemetry, UI_EVENT, type UiTelemetry } from './logSinkTelemetry.ts';\nimport { DEFAULT_THEME_NAME } from './theme.ts';\nimport { DoomUiState, type LeaderSnapshot } from './uiState.ts';\n\nconst LEADER_WIDGET_KEY = 'doom-pi-leader';\nconst TUI_MODE = 'tui';\nconst WIDGET_PLACEMENT = 'belowEditor';\nconst WARNING_STYLE = 'warning';\nconst THEME_ENVIRONMENT_KEY = 'DOOMPI_THEME';\nconst HOTKEYS_COMMAND = 'hotkeys';\nconst TOOLS_COMMAND = 'tools';\nconst CONFIG_COMMAND = 'config';\n/** Short: `/tools` must open promptly whether or not an MCP extension answers. */\nconst MCP_STATUS_TIMEOUT_MS = 250;\nconst SESSION_START_EVENT = 'session_start';\nconst SESSION_MESSAGE_ENTRY = 'message';\nconst INPUT_EVENT = 'input';\nconst INTERACTIVE_INPUT_SOURCE = 'interactive';\nconst SESSION_SHUTDOWN_EVENT = 'session_shutdown';\nconst TITLE_MESSAGE_MAX_LENGTH = 64;\nconst TITLE_PREFIX = 'doom-pi';\nconst BUILTIN_LEADER_COMMANDS = new Set([HOTKEYS_COMMAND]);\n\nfunction createMessageTitle(message: string, cwd: string): string | undefined {\n const normalizedMessage = message.trim().replace(/\\s+/g, ' ');\n if (!normalizedMessage) return undefined;\n const messageTitle = normalizedMessage.slice(0, TITLE_MESSAGE_MAX_LENGTH).trimEnd();\n return `${TITLE_PREFIX} · ${messageTitle} · ${path.basename(cwd) || cwd}`;\n}\n\nexport function doomPiUiExtension(pi: ExtensionAPI, telemetry: UiTelemetry = createUiTelemetry()): void {\n const uiState = new DoomUiState();\n const footerStatusRegistry = new DoomFooterStatusRegistry();\n const modeStatusRegistry = new DoomModeStatusRegistry();\n const configRegistry = new DoomConfigRegistry();\n const leaderRegistry = new DoomLeaderRegistry();\n const pendingDiagnostics = new Set<string>();\n const contributionGenerations = new Map<string, symbol>();\n let footerData: ReadonlyFooterDataProvider | undefined;\n let activeContext: ExtensionContext | undefined;\n let disposeFooterStatuses: (() => void) | undefined;\n let disposeModeStatuses: (() => void) | undefined;\n let disposeModeSubscription: (() => void) | undefined;\n let disposeConfigContributions: (() => void) | undefined;\n let disposeLeaderRegistration: (() => void) | undefined;\n let disposeLeaderUnregistration: (() => void) | undefined;\n const protocolContributions = new Map<string, () => void>();\n let nextProtocolHandle = 0;\n let sessionGeneration = 0;\n let hasSessionMessage = false;\n let sessionProtocol: ProtocolRuntime | undefined;\n\n /**\n * Asks the MCP extension what it is currently connected to.\n *\n * No answer is a normal session: `--no-mcp` leaves nobody providing the query,\n * and the panel simply lists MCP tools under the extension that registered them.\n */\n const readMcpServers = async (): Promise<readonly McpServerStatus[] | undefined> => {\n if (!sessionProtocol) return undefined;\n try {\n const snapshot = await sessionProtocol.request(McpStatusQueryProtocol, {}, { timeoutMs: MCP_STATUS_TIMEOUT_MS });\n return snapshot.servers;\n } catch {\n return undefined;\n }\n };\n\n pi.registerCommand(TOOLS_COMMAND, {\n description: 'Browse the tools available in this session',\n handler: async (_args, ctx) => {\n // Read at open time so a reconnected server or a plan-mode tool swap shows\n // up without restarting the session.\n const mcpServers = await readMcpServers();\n const sources = buildToolSources({\n tools: pi.getAllTools(),\n activeTools: pi.getActiveTools(),\n ...(mcpServers ? { mcpServers } : {}),\n });\n await openToolsOverlay(ctx, sources);\n },\n });\n\n pi.registerCommand(CONFIG_COMMAND, {\n description: 'Browse and change the settings extensions contribute',\n handler: async (_args, ctx) => {\n // The registry is passed live rather than snapshotted: a contributor can be\n // fetching or installing something while the panel is open, and that\n // progress is meant to land in it.\n await openConfigOverlay(ctx, configRegistry);\n },\n });\n\n const reportDiagnostics = (diagnostics: readonly DoomLeaderDiagnostic[]): void => {\n for (const diagnostic of diagnostics) {\n const binding = diagnostic.bindingId ? ` (${diagnostic.bindingId})` : '';\n const message = `Leader contribution from ${diagnostic.source}${binding}: ${diagnostic.message}`;\n if (pendingDiagnostics.has(message)) continue;\n pendingDiagnostics.add(message);\n // A rejected contribution silently drops another extension's keybindings,\n // and the notice below only reaches a run that has a TUI attached.\n void telemetry.recordWarning(UI_EVENT.leaderContributionRejected, diagnostic.message, {\n 'leader.source': diagnostic.source,\n ...(diagnostic.bindingId ? { 'leader.binding.id': diagnostic.bindingId } : {}),\n });\n if (activeContext?.mode === TUI_MODE) activeContext.ui.notify(message, WARNING_STYLE);\n }\n };\n\n const leaderContext = Object.freeze({\n register(contribution: LeaderContribution) {\n const generation = Symbol(contribution.source);\n const result = leaderRegistry.register(contribution);\n reportDiagnostics(result.diagnostics);\n if (!result.accepted) return () => undefined;\n contributionGenerations.set(contribution.source, generation);\n let disposed = false;\n return () => {\n if (disposed) return;\n disposed = true;\n if (contributionGenerations.get(contribution.source) !== generation) return;\n contributionGenerations.delete(contribution.source);\n leaderRegistry.register({ source: contribution.source, bindings: [] });\n };\n },\n });\n pi.on(SESSION_START_EVENT, (_event, ctx) => {\n activeContext = ctx;\n const activeGeneration = ++sessionGeneration;\n const protocol = createProtocolRuntime({\n bus: pi.events,\n source: '@agimon-ai/doompi-ui',\n sessionId: ctx.sessionManager.getSessionId(),\n });\n sessionProtocol = protocol;\n disposeFooterStatuses?.();\n footerStatusRegistry.reset();\n disposeFooterStatuses = footerStatusRegistry.bind(protocol);\n disposeModeStatuses?.();\n modeStatusRegistry.reset();\n disposeModeStatuses = modeStatusRegistry.bind(protocol);\n // Bound above the TUI-mode guard below, like footer and mode: a headless run\n // still accepts contributions, it just never draws them.\n disposeConfigContributions?.();\n configRegistry.reset();\n disposeConfigContributions = configRegistry.bind(protocol);\n disposeLeaderRegistration?.();\n disposeLeaderUnregistration?.();\n disposeLeaderRegistration = protocol.provide(\n LeaderRegistrationProtocol,\n ({ contribution, generation }, identity) => {\n if (identity.source !== contribution.source) {\n throw new Error('Leader contribution source does not match the registration envelope source.');\n }\n const handleId = `${identity.source}:${generation}:${++nextProtocolHandle}`;\n const typedContribution: LeaderContribution = {\n source: contribution.source,\n bindings: contribution.bindings.map((binding) => {\n const [first, ...rest] = binding.path;\n if (!first) throw new Error(`Leader binding '${binding.id}' has no path.`);\n return { ...binding, path: [first, ...rest] };\n }),\n };\n protocolContributions.set(handleId, leaderContext.register(typedContribution));\n return { handleId };\n },\n );\n disposeLeaderUnregistration = protocol.provide(LeaderUnregistrationProtocol, ({ handleId }) => {\n const dispose = protocolContributions.get(handleId);\n if (!dispose) return { removed: false };\n protocolContributions.delete(handleId);\n dispose();\n return { removed: true };\n });\n hasSessionMessage = ctx.sessionManager.getEntries().some((entry) => entry.type === SESSION_MESSAGE_ENTRY);\n if (ctx.mode !== TUI_MODE) return;\n for (const message of pendingDiagnostics) ctx.ui.notify(message, WARNING_STYLE);\n\n const themeName = process.env[THEME_ENVIRONMENT_KEY] || DEFAULT_THEME_NAME;\n const themeResult = ctx.ui.setTheme(themeName);\n if (!themeResult.success) {\n ctx.ui.notify(`Could not apply Doom Pi theme: ${themeResult.error}`, WARNING_STYLE);\n void telemetry.recordWarning(UI_EVENT.themeApplyFailed, themeResult.error ?? 'Unknown theme error', {\n 'ui.theme': themeName,\n });\n }\n\n // The modes go into shared state rather than a widget of their own: the\n // editor prints their names beside its badge and the leader panel prints\n // them with their detail, so there is no third surface to paint.\n disposeModeSubscription?.();\n disposeModeSubscription = modeStatusRegistry.subscribe(() => uiState.setModes(modeStatusRegistry.getStatuses()));\n uiState.setModes(modeStatusRegistry.getStatuses());\n\n ctx.ui.setTitle(`${TITLE_PREFIX} · ${path.basename(ctx.cwd) || ctx.cwd} · pi/coding`);\n ctx.ui.setHeader((tui, theme) => new DoomHeader(theme, ctx.cwd, undefined, ctx, () => tui.terminal.rows));\n ctx.ui.setFooter((tui, theme, data) => {\n footerData = data;\n return new DoomFooter(tui, theme, ctx, data, footerStatusRegistry, uiState);\n });\n ctx.ui.setEditorComponent(\n (tui, editorTheme, keybindings) =>\n new DoomEditor(\n tui,\n editorTheme,\n keybindings,\n uiState,\n (snapshot: LeaderSnapshot) => {\n if (!snapshot.active) {\n ctx.ui.setWidget(LEADER_WIDGET_KEY, undefined, { placement: WIDGET_PLACEMENT });\n return;\n }\n ctx.ui.setWidget(\n LEADER_WIDGET_KEY,\n (_widgetTui, widgetTheme) => new LeaderHints(widgetTheme, snapshot, ctx, footerData, uiState.getModes()),\n { placement: WIDGET_PLACEMENT },\n );\n },\n undefined,\n ctx.ui.theme,\n {\n registry: leaderRegistry,\n isCommandAvailable: (commandName) =>\n BUILTIN_LEADER_COMMANDS.has(commandName) ||\n pi.getCommands().some((command) => command.name === commandName),\n onLeaderAction: (source, action) => {\n if (activeContext !== ctx || sessionGeneration !== activeGeneration) {\n ctx.ui.notify(`Leader action ${action} is unavailable.`, WARNING_STYLE);\n return;\n }\n protocol.notify(LeaderActionInvoked, { source, action });\n },\n onUnavailableCommand: (commandName) => {\n void telemetry.recordWarning(\n UI_EVENT.leaderCommandUnavailable,\n `Leader command /${commandName} is unavailable.`,\n { 'ui.kind': 'command' },\n );\n ctx.ui.notify(`Leader command /${commandName} is unavailable.`, WARNING_STYLE);\n },\n onUnavailableAction: (action) => {\n void telemetry.recordWarning(\n UI_EVENT.leaderActionUnavailable,\n `Leader action ${action} is unavailable.`,\n {\n 'ui.kind': 'action',\n },\n );\n ctx.ui.notify(`Leader action ${action} is unavailable.`, WARNING_STYLE);\n },\n },\n ),\n );\n\n void telemetry.recordEvent(UI_EVENT.shellInstalled, {\n 'ui.theme': themeName,\n 'ui.theme.applied': themeResult.success,\n 'ui.leader.group.count': leaderRegistry.getGroup([])?.options.length ?? 0,\n });\n });\n\n pi.on(INPUT_EVENT, (event, ctx) => {\n if (ctx.mode !== TUI_MODE || event.source !== INTERACTIVE_INPUT_SOURCE || hasSessionMessage) return;\n hasSessionMessage = true;\n const title = createMessageTitle(event.text, ctx.cwd);\n if (title) ctx.ui.setTitle(title);\n });\n\n pi.on(SESSION_SHUTDOWN_EVENT, async (_event, ctx) => {\n ++sessionGeneration;\n for (const dispose of protocolContributions.values()) dispose();\n protocolContributions.clear();\n activeContext = undefined;\n footerData = undefined;\n disposeFooterStatuses?.();\n disposeFooterStatuses = undefined;\n footerStatusRegistry.reset();\n disposeModeStatuses?.();\n disposeModeStatuses = undefined;\n disposeModeSubscription?.();\n disposeModeSubscription = undefined;\n modeStatusRegistry.reset();\n disposeConfigContributions?.();\n disposeConfigContributions = undefined;\n disposeLeaderRegistration?.();\n disposeLeaderRegistration = undefined;\n disposeLeaderUnregistration?.();\n disposeLeaderUnregistration = undefined;\n configRegistry.reset();\n uiState.reset();\n sessionProtocol = undefined;\n if (ctx.mode === TUI_MODE) ctx.ui.setWidget(LEADER_WIDGET_KEY, undefined, { placement: WIDGET_PLACEMENT });\n await telemetry.shutdown();\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./_virtual/_rolldown/runtime.cjs");let t=require("node:path");t=e.__toESM(t,1);let n=require("node:fs");n=e.__toESM(n,1);const r=new Map;function i(e){let r=e;for(;;){try{let e=JSON.parse(n.default.readFileSync(t.default.join(r,`package.json`),`utf8`));if(typeof e.name==`string`&&e.name.length>0)return e.name}catch{}let e=t.default.dirname(r);if(e===r)return;r=e}}function a(e){let n=r.get(e);if(n!==void 0)return n;let a=i(t.default.dirname(e)),o=a?a.split(`/`).pop()??a:t.default.basename(e,t.default.extname(e));return r.set(e,o),o}exports.extensionName=a;
|
|
2
|
+
//# sourceMappingURL=extensionName.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensionName.cjs","names":["fs","path"],"sources":["../src/extensionName.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\n/**\n * Display name for an extension that Pi could not attribute to a package.\n *\n * Pi stamps every `--extension <path>` argument with the literal source `cli`,\n * and doom-pi launches all of its extensions that way, so the only surviving\n * identity is the entry path. The nearest `package.json` above it names the\n * package that registered the tools.\n */\n\n/** Results are stable for the life of a session, and every source hits the same few dirs. */\nconst cache = new Map<string, string>();\n\nfunction packageName(directory: string): string | undefined {\n let current = directory;\n for (;;) {\n try {\n const manifest = JSON.parse(fs.readFileSync(path.join(current, 'package.json'), 'utf8')) as { name?: unknown };\n if (typeof manifest.name === 'string' && manifest.name.length > 0) return manifest.name;\n } catch {\n // No manifest here, or an unreadable one; keep walking up.\n }\n const parent = path.dirname(current);\n if (parent === current) return undefined;\n current = parent;\n }\n}\n\nexport function extensionName(entryPath: string): string {\n const cached = cache.get(entryPath);\n if (cached !== undefined) return cached;\n const name = packageName(path.dirname(entryPath));\n // `@agimon-ai/doompi-task` reads as `doom-task`; the scope is noise in a tree.\n const resolved = name ? (name.split('/').pop() ?? name) : path.basename(entryPath, path.extname(entryPath));\n cache.set(entryPath, resolved);\n return resolved;\n}\n"],"mappings":"6MAaA,MAAM,EAAQ,IAAI,IAElB,SAAS,EAAY,EAAuC,CAC1D,IAAI,EAAU,EACd,OAAS,CACP,GAAI,CACF,IAAM,EAAW,KAAK,MAAMA,EAAAA,QAAG,aAAaC,EAAAA,QAAK,KAAK,EAAS,cAAc,EAAG,MAAM,CAAC,EACvF,GAAI,OAAO,EAAS,MAAS,UAAY,EAAS,KAAK,OAAS,EAAG,OAAO,EAAS,IACrF,MAAQ,CAER,CACA,IAAM,EAASA,EAAAA,QAAK,QAAQ,CAAO,EACnC,GAAI,IAAW,EAAS,OACxB,EAAU,CACZ,CACF,CAEA,SAAgB,EAAc,EAA2B,CACvD,IAAM,EAAS,EAAM,IAAI,CAAS,EAClC,GAAI,IAAW,IAAA,GAAW,OAAO,EACjC,IAAM,EAAO,EAAYA,EAAAA,QAAK,QAAQ,CAAS,CAAC,EAE1C,EAAW,EAAQ,EAAK,MAAM,GAAG,CAAC,CAAC,IAAI,GAAK,EAAQA,EAAAA,QAAK,SAAS,EAAWA,EAAAA,QAAK,QAAQ,CAAS,CAAC,EAE1G,OADA,EAAM,IAAI,EAAW,CAAQ,EACtB,CACT"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensionName.d.cts","names":[],"sources":["../src/extensionName.ts"],"mappings":";iBA8BgB,cAAc"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensionName.d.mts","names":[],"sources":["../src/extensionName.ts"],"mappings":";iBA8BgB,cAAc"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import e from"node:path";import t from"node:fs";const n=new Map;function r(n){let r=n;for(;;){try{let n=JSON.parse(t.readFileSync(e.join(r,`package.json`),`utf8`));if(typeof n.name==`string`&&n.name.length>0)return n.name}catch{}let n=e.dirname(r);if(n===r)return;r=n}}function i(t){let i=n.get(t);if(i!==void 0)return i;let a=r(e.dirname(t)),o=a?a.split(`/`).pop()??a:e.basename(t,e.extname(t));return n.set(t,o),o}export{i as extensionName};
|
|
2
|
+
//# sourceMappingURL=extensionName.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensionName.mjs","names":[],"sources":["../src/extensionName.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\n/**\n * Display name for an extension that Pi could not attribute to a package.\n *\n * Pi stamps every `--extension <path>` argument with the literal source `cli`,\n * and doom-pi launches all of its extensions that way, so the only surviving\n * identity is the entry path. The nearest `package.json` above it names the\n * package that registered the tools.\n */\n\n/** Results are stable for the life of a session, and every source hits the same few dirs. */\nconst cache = new Map<string, string>();\n\nfunction packageName(directory: string): string | undefined {\n let current = directory;\n for (;;) {\n try {\n const manifest = JSON.parse(fs.readFileSync(path.join(current, 'package.json'), 'utf8')) as { name?: unknown };\n if (typeof manifest.name === 'string' && manifest.name.length > 0) return manifest.name;\n } catch {\n // No manifest here, or an unreadable one; keep walking up.\n }\n const parent = path.dirname(current);\n if (parent === current) return undefined;\n current = parent;\n }\n}\n\nexport function extensionName(entryPath: string): string {\n const cached = cache.get(entryPath);\n if (cached !== undefined) return cached;\n const name = packageName(path.dirname(entryPath));\n // `@agimon-ai/doompi-task` reads as `doom-task`; the scope is noise in a tree.\n const resolved = name ? (name.split('/').pop() ?? name) : path.basename(entryPath, path.extname(entryPath));\n cache.set(entryPath, resolved);\n return resolved;\n}\n"],"mappings":"gDAaA,MAAM,EAAQ,IAAI,IAElB,SAAS,EAAY,EAAuC,CAC1D,IAAI,EAAU,EACd,OAAS,CACP,GAAI,CACF,IAAM,EAAW,KAAK,MAAM,EAAG,aAAa,EAAK,KAAK,EAAS,cAAc,EAAG,MAAM,CAAC,EACvF,GAAI,OAAO,EAAS,MAAS,UAAY,EAAS,KAAK,OAAS,EAAG,OAAO,EAAS,IACrF,MAAQ,CAER,CACA,IAAM,EAAS,EAAK,QAAQ,CAAO,EACnC,GAAI,IAAW,EAAS,OACxB,EAAU,CACZ,CACF,CAEA,SAAgB,EAAc,EAA2B,CACvD,IAAM,EAAS,EAAM,IAAI,CAAS,EAClC,GAAI,IAAW,IAAA,GAAW,OAAO,EACjC,IAAM,EAAO,EAAY,EAAK,QAAQ,CAAS,CAAC,EAE1C,EAAW,EAAQ,EAAK,MAAM,GAAG,CAAC,CAAC,IAAI,GAAK,EAAQ,EAAK,SAAS,EAAW,EAAK,QAAQ,CAAS,CAAC,EAE1G,OADA,EAAM,IAAI,EAAW,CAAQ,EACtB,CACT"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pi.cjs","names":["doomPiUiExtension"],"sources":["../../src/extensions/pi.ts"],"sourcesContent":["import { doomPiUiExtension } from '../extension.ts';\n\nexport default doomPiUiExtension;\n"],"mappings":"AAEA,IAAA,4BAAeA,CAAAA,CAAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pi.mjs","names":[],"sources":["../../src/extensions/pi.ts"],"sourcesContent":["import { doomPiUiExtension } from '../extension.ts';\n\nexport default doomPiUiExtension;\n"],"mappings":"qDAEA,IAAA,EAAe"}
|
package/dist/footer.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./_virtual/_rolldown/runtime.cjs");var t=e.__exportAll({});e.__reExport(t,require("@agimon-ai/doompi-extension-contracts/footer")),Object.defineProperty(exports,"footer_exports",{enumerable:!0,get:function(){return t}});var n=require("@agimon-ai/doompi-extension-contracts/footer");Object.keys(n).forEach(function(e){e!=="default"&&!Object.prototype.hasOwnProperty.call(exports,e)&&Object.defineProperty(exports,e,{enumerable:!0,get:function(){return n[e]}})});
|
|
2
|
+
//# sourceMappingURL=footer.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"footer.cjs","names":[],"sources":["../src/footer.ts"],"sourcesContent":["export * from '@agimon-ai/doompi-extension-contracts/footer';\n"],"mappings":""}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export * from "@agimon-ai/doompi-extension-contracts/footer";
|
|
2
|
+
import * as import__agimon_ai_doompi_extension_contracts_footer from "@agimon-ai/doompi-extension-contracts/footer";
|
|
3
|
+
//#endregion
|
|
4
|
+
export { import__agimon_ai_doompi_extension_contracts_footer as footer_d_exports };
|
|
5
|
+
//# sourceMappingURL=footer.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"footer.d.cts","names":[],"sources":["../src/footer.ts"],"mappings":""}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export * from "@agimon-ai/doompi-extension-contracts/footer";
|
|
2
|
+
import * as import__agimon_ai_doompi_extension_contracts_footer from "@agimon-ai/doompi-extension-contracts/footer";
|
|
3
|
+
//#endregion
|
|
4
|
+
export { import__agimon_ai_doompi_extension_contracts_footer as footer_d_exports };
|
|
5
|
+
//# sourceMappingURL=footer.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"footer.d.mts","names":[],"sources":["../src/footer.ts"],"mappings":""}
|